Bringing electricity information from eloverblik.dk and energidataservice.dk into Openhab

  1. What kind of heating system is this Nilan device you’re planning to control? I have a Nibe F-1226 ground source heat pump which is NOT a so called inverter pump like most modern heat pumps are.

In other words, my heat pump is a traditional on/off pump whereas modern inverter pumps adjust the speed instead of toggling the compressor on/off.

There are different opinions/schools in the heat pump forums. One quite popular opinion related to on/off pumps is that trying to minimize the nimber of compressor starts will make it last longer. In other words, longer consequtive heating periods is preferred over many short periods.

  1. Regarding the ventilation: I don’t recommend you to interfere with this. The benefit of that is not worth it in my opinion compared to the downsides (decreased quality of the air, messed up humidity etc).

Cheers,
Markus

P.s. you might want to open a separate thread for heat pump control strategies, since that is easily a topic of its own. You can ping me there and I’ll be happy to share what I’ve learnt about heat pumps.

Looks good. :+1:

I’m wondering now if you need more history to fill out all hours for Today? Currently I populate prices only 12 hours back in time, but that’s an arbitrary number I selected just to have some kind of restriction/boundary. My own use-case for keeping historic prices at all is to be able to show the total price when the tumble dryer has finished: Dishwasher price calculation automation - #5 by laursen

My rule triggers when HourlyPrice changes, so I think it will catch the older prices if it updates frequently. How often does it update (or what event triggers an update)?
I’m trying to add VAT to my items with format already, the following doesn’t seem to work, I guess I misunderstand how to use it, any suggestion?

Number    ElectricityPriceAt01Today       "Price Today at 01:00 [VAT(25):%.2f kr]"       (gEnergiDataServiceToday)

It’s currently not possible to do that for future prices, only historic prices which are persisted hour by hour. That’s one of the things I expect to be solved by the mentioned core pull request 3597.

That should be possible with calculateCheapestPeriod, although I’d be interested to know if it fully covers your case. You probably know how much you want to charge it and either the charge speed or perhaps even the duration needed? Depending on the information you have available to making the decision, we might find some calculation methods missing.

Btw, I consider the calculations within the binding temporary as they don’t really belong there. Still, for the time being they are better to have there than scattered even more in user rules etc. End goal:

1 Like

Thank you for your reply!

I’m actually not sure what type of heat pump it is - it’s Nilan Compact P Geo 6. I asked the support today and they only suggest to pause the heating and hot water when on vacation for days. They told me if the heat pump is only off for some hours, it might take more energy to restore to the original temperature.

I will never turn off ventilation - as I understand it can cause serious damage especially during winter time when condensation can form inside the pipe which will cause fungus and other problems.

My current consumption is around 10 kWh during summer and 30kWh during winter per day. So maybe not much to save there. Today I tried to turn off hot water production, since we only have 180L tank now the temp drops to 18 degree and I have to enable electrical heater (1500w).

Yes, that should be sufficient then if your items are persisted. If not, you will lack some of the hours after a restart.

As rarely as possible. :slight_smile: Spot prices will be fetched around 13:00 each day. Tariffs etc. are fetched upon expiration, i.e. the binding will make sure to always have tomorrow cached as well.

FYI, the documentation is here: VAT - Transformation Services | openHAB

Just checking, have you installed the VAT transformation service?

There could be some issue when the date rolls - I need to find a smart way to remove prices from all items tomorrow I guess. The best way to do it is to remove all prices first and add them back based on the new date/getPrices, however it will give issue if there are only 12 hours history. Also if HourlyPrice is only updated once at 13:00 per day, I might miss an hour price at 00:00 (midnight). It would be nice to include all hours for “today” I think - it’s better than going back 12/24 hours as in that case the date can be yesterday.

Yes the VAT transformer is installed and working correctly for other items like spot price. I cannot use ItemChannelLink as my items don’t have channels, so I took the display example, maybe it’s only for display in UI (sorry I never use the UI part of OpenHAB)?

Thank you!

Yes, at least you could verify if it’s working by checking the UI. To apply VAT to your 48 channels, you might want to do that directly in your rule by:

transform("VAT", "DK", price.toString)

You could have a rule triggered at midnight (by cron event) which will copy “tomorrow” items into “today” items and set “tomorrow” items to UNDEF, i.e. to do the roll. Another trigger could be when an item linked to channel hourly-prices changed, i.e. when the prices are updated around 13:00. This could then update “tomorrow” items as this is the moment when they become available. This would also ensure it would work if the spot prices are not available at 13:00 for any reason - as soon as they will be, you will have your items updated.

I will update the binding to keep 12 hours of history or all hours today - whichever will be the most.

1 Like

I checked the event log, and it looks like HourlyPrice is updated once per hour at XX:00:00. I have modified my rule to the following which also reset tmr prices if none is in the price feed:

rule "Update electricity price list"
when     
    Item HourlyPrices changed
    or Time cron "0 * * * * ? *"
then
    val actions = getActions("energidataservice", "energidataservice:service:energidataservice")
    var priceMap = actions.getPrices(null)
    var DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    var LocalDateTime today = LocalDateTime.now()
    var LocalDateTime tomorrow = today.plusDays(1)
    var Boolean isTmrPriceAvailable = false
    var Boolean isTmrPriceDefined = gEnergiDataServiceTomorrow.members.filter[ item | item.state == UNDEF ].size == 0
    priceMap.forEach[ key, value | 
        var LocalDateTime ldt = LocalDateTime.ofInstant(key, ZoneOffset.systemDefault())
        var DateTimeFormatter hourFormatter = DateTimeFormatter.ofPattern("HH");
        var String dateStr = ldt.format(dateFormatter)
        var String hourStr = ldt.format(hourFormatter)
        var String itemName = "PriceAt" + hourStr
        if(dateStr == tomorrow.format(dateFormatter)) {
            isTmrPriceAvailable = true
  			gEnergiDataServiceTomorrow.members.findFirst[name.equals(itemName+"Tomorrow")].postUpdate(transform("VAT", "DK", value.toString))
        }
        else if (dateStr == today.format(dateFormatter)){
            gEnergiDataServiceToday.members.findFirst[name.equals(itemName+"Today")].postUpdate(transform("VAT", "DK", value.toString))
        }
    ]
    if (!isTmrPriceAvailable) {
        gEnergiDataServiceTomorrow.members.forEach[ item | 
            item.postUpdate(UNDEF)
        ]
        logInfo("EnergiDataService", "Reset tomorrow's electricity price to UNDEF")
    } else if (!isTmrPriceDefined) {
        logInfo("EnergiDataService", "Received tomorrow's electricity prices")
        sendNotification("xxxxxx", "Electricity for tomorrow is delivered at " + String.format("%1$tH:%1$tM", now))
    }
end
2 Likes

Hi.

I’ve been trying to make this binding for my OH 3.4 instance but struggling a little to build, as I have not done this before directly from the openhab-addons repo. Perhaps you can point me in the right direction.

I’m using VSC on a windows machine - I have maven installed and can build the Z-wave binding which I have, but from another repo.

What I have tried:
1 - download only the energidataservice binding folder. Failed to build - something with dependecies not being found.

2 - Fork the entire openhab-addons repo from the main branch - which does not include your binding. Copying your binding into that repo, and try and build. This showed the followinng message (I have not included all dependencies, but there are some 30-50 with the same absent message:

Could not resolve dependencies for project org.openhab.addons.bundles:org.openhab.binding.energidataservice:jar:4.0.0-SNAPSHOT: The following artifacts could not be resolved: org.openhab.core.bundles:org.openhab.core.test:jar:4.0.0-SNAPSHOT (absent)

3 - I then tried to change the pom.xml file where I changed the version to what I am currently using:

  <parent>
    <groupId>org.openhab.addons.bundles</groupId>
    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
    <version>3.4.3-SNAPSHOT</version>
  </parent>

this then gave the following build error message (again only the first dependecy is shown - the list contains the same 30-50 dependencies - all with the same message):

Could not resolve dependencies for project org.openhab.addons.bundles:org.openhab.binding.energidataservice:jar:4.0.0-SNAPSHOT: The following artifacts could not be resolved: org.openhab.core.bundles:org.openhab.core.test:jar:4.0.0-SNAPSHOT (present)

I am running the release build of OH 3.4.3 on my RPi, but should this be an issue?

Hope you can help point me in the right direction
BR
Mark

The branch jlaur:14222-energidataservice is targeting the main branch, i.e. openHAB 4.0. It requires Java 17 as well as Gson 2.10. Therefore you cannot build it for 3.4.

I have backported it to a private branch. You can find a JAR that can be used directly with openHAB 3.4 here: Energi Data Service Binding [3.4.0.0;4.0.0.0)

You can also install it straight from the UI under Community Add-ons.

Thanks - I have no clue as to why I didn’t just check the UI first :person_facepalming: :laughing:

Just want to report in the past week the binding works flawlessly for me. Thank you for the great effort @laursen !
I received next day’s EL price every day at 13:00 - it is a big improvement over NordPool which I sometimes experience some delay. Maybe a small improvement suggestion: in case the price for next day is delivered earlier (NordPool sometimes delivers prices already at 12:40), it would be nice to trigger an update of HourlyPrices - instead of always update the item hourly.
The calculation for the cheapest period for charging my car also works perfect. The only issue I found is sometimes when I create an Instant for midnight, if I don’t set nanoseconds it doesn’t return a period for charging - I think maybe it’s because when nanoseconds > 0 it consider a new day and hence the algorithm doesn’t work to find the cheapest period. Anyways it’s easy fix :slight_smile:

Thanks for reporting back, I’m glad to hear it’s working as expected. :slight_smile:

All channels are updated as soon as new prices are available in the binding. However, since the prices are not guaranteed to be available before 13:00, the binding won’t try to fetch them before that time. In fact, I even add a small amount “jitter”, i.e. a random amount of time, so that you will receive the update between 13:00 and 13:01. This is to ensure that everyone will not call the service at the exact same time.

I know that prices are often available a little before 13:00. However, I’m afraid that trying to fetch them before the official availability time will only lead to more calls when they are not yet available, because the binding would then have to retry.

So even though you see hourly updates, in fact the binding only calls the service one time per day. The prices are then cached, and a scheduled job will update the channels accordingly each hour.

Is it a real problem waiting until 13:00-13:01, or only an observation? The time is kind of arbitrary, so for automations it probably shouldn’t make much of a difference waiting up to 20 minutes too much for next day’s prices. I understand though (believe me) that it can be slightly annoying waiting until 13:00 when getting the idea to check the prices at 12:50 and knowing they are probably available already, but that’s more a psychological problem. :wink:

Can you paste your rule code, then I’ll check that?

Looks like today there has been some problem with prices for tomorrow - now it’s 14:05 but it still hasn’t come. Do you mean you only try to call the service once per day? Does it mean if the delivery from NordPool is delayed after 13:00, the binding will never update tomorrow’s price (seems like my observation today)?
On a separate note: wouldn’t be a good idea to update the channels when the binding receives tomorrow’s price immediately (instead of caching and update per hourly schedule)?
Have a nice weekend!

Yes, I can see that:

2023-05-26 13:00:46.289 [TRACE] [gidataservice.internal.ApiController] - GET request for https://api.energidataservice.dk/dataset/Elspotprices?start=utcnow&filter=%7B%22PriceArea%22%3A%22DK1%22%7D&columns=HourUTC%2CSpotPriceDKK
2023-05-26 13:00:49.531 [TRACE] [gidataservice.internal.ApiController] - Response content: '{"total":13,"filters":"{\"PriceArea\":\"DK1\"}","dataset":"Elspotprices","records":[{"HourUTC":"2023-05-26T21:00:00","SpotPriceDKK":689.130005},{"HourUTC":"2023-05-26T20:00:00","SpotPriceDKK":741.280029},{"HourUTC":"2023>
2023-05-26 13:00:49.561 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Price update job rescheduled in 3550439 milliseconds
2023-05-26 13:00:49.573 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Refresh job rescheduled in 706 seconds: 2023-05-26T11:12:35.573561Z

So the call was successful, but we didn’t get new prices for tomorrow, and therefore it was rescheduled for being tried again 13:12:35:

2023-05-26 13:12:35.575 [TRACE] [gidataservice.internal.ApiController] - GET request for https://api.energidataservice.dk/dataset/Elspotprices?start=utcnow&filter=%7B%22PriceArea%22%3A%22DK1%22%7D&columns=HourUTC%2CSpotPriceDKK
2023-05-26 13:12:36.162 [TRACE] [gidataservice.internal.ApiController] - Response content: '{"total":12,"filters":"{\"PriceArea\":\"DK1\"}","dataset":"Elspotprices","records":[{"HourUTC":"2023-05-26T21:00:00","SpotPriceDKK":689.130005},{"HourUTC":"2023-05-26T20:00:00","SpotPriceDKK":741.280029},{"HourUTC":"2023>
2023-05-26 13:12:36.190 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Price update job rescheduled in 2843810 milliseconds
2023-05-26 13:12:36.193 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Refresh job rescheduled in 1316 seconds: 2023-05-26T11:34:32.192835Z

Same situation, again scheduled for 13:34:32:

2023-05-26 13:34:32.194 [TRACE] [gidataservice.internal.ApiController] - GET request for https://api.energidataservice.dk/dataset/Elspotprices?start=utcnow&filter=%7B%22PriceArea%22%3A%22DK1%22%7D&columns=HourUTC%2CSpotPriceDKK
2023-05-26 13:34:32.823 [TRACE] [gidataservice.internal.ApiController] - Response content: '{"total":12,"filters":"{\"PriceArea\":\"DK1\"}","dataset":"Elspotprices","records":[{"HourUTC":"2023-05-26T21:00:00","SpotPriceDKK":689.130005},{"HourUTC":"2023-05-26T20:00:00","SpotPriceDKK":741.280029},{"HourUTC":"2023>
2023-05-26 13:34:32.849 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Price update job rescheduled in 1527152 milliseconds
2023-05-26 13:34:32.850 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Refresh job rescheduled in 2600 seconds: 2023-05-26T12:17:52.850701Z

Same situation, again scheduled for 14:17:52:

2023-05-26 14:17:52.851 [TRACE] [gidataservice.internal.ApiController] - GET request for https://api.energidataservice.dk/dataset/Elspotprices?start=utcnow&filter=%7B%22PriceArea%22%3A%22DK1%22%7D&columns=HourUTC%2CSpotPriceDKK
2023-05-26 14:17:53.503 [TRACE] [gidataservice.internal.ApiController] - Response content: '{"total":11,"filters":"{\"PriceArea\":\"DK1\"}","dataset":"Elspotprices","records":[{"HourUTC":"2023-05-26T21:00:00","SpotPriceDKK":689.130005},{"HourUTC":"2023-05-26T20:00:00","SpotPriceDKK":741.280029},{"HourUTC":"2023>
2023-05-26 14:17:53.530 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Price update job rescheduled in 2526471 milliseconds
2023-05-26 14:17:53.531 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Refresh job rescheduled in 5625 seconds: 2023-05-26T13:51:38.531423Z

Still no new prices, next retry 15:51:38. And now finally:

2023-05-26 15:51:45.476 [TRACE] [gidataservice.internal.ApiController] - GET request for https://api.energidataservice.dk/dataset/Elspotprices?start=utcnow&filter=%7B%22PriceArea%22%3A%22DK1%22%7D&columns=HourUTC%2CSpotPriceDKK
2023-05-26 15:51:54.713 [TRACE] [gidataservice.internal.ApiController] - Response content: '{"total":34,"filters":"{\"PriceArea\":\"DK1\"}","dataset":"Elspotprices","records":[{"HourUTC":"2023-05-27T21:00:00","SpotPriceDKK":705.489990},{"HourUTC":"2023-05-27T20:00:00","SpotPriceDKK":745.109985},{"HourUTC":"2023>
2023-05-26 15:51:57.787 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Price update job rescheduled in 482213 milliseconds
2023-05-26 15:51:57.795 [DEBUG] [nal.handler.EnergiDataServiceHandler] - Refresh job rescheduled in 76123 seconds: 2023-05-27T11:00:40.794798Z

This time we received all the prices expected, so next call with be 13:00:40 tomorrow.

If the call is successful and we get tomorrow’s prices as expected, yes, then no further calls will be made. If the call fails, or if we didn’t get the expected prices for tomorrow, there will be further attempts. The time until next attempt depends on the situation, but in both cases there is an exponential back-off strategy with different parameters (aggresiveness).

No, it never gives up. There are properties available allowing you to track the status:

The internal cache and the channel hourly-prices are updated immediately when new data is received. The current price (e.g. channel spot-price) is updated each hour when the current price changes. In the future, when this work is finished:

I will update the binding so that future prices are also pushed immediately, so that they can be persisted.

The same to you!

For me DK2 prices were updated between 14:00 and 15:00, and the HourlyPrices were changed at 15:00. I don’t know when the binding receives the price though.
I noticed https://elspotpris.dk have tomorrow price delivered before 14:00 though (so NordPool has the prices before 14:00 and EnergiDataService for some reason is a bit later). I don’t know why DK1 prices you query came at first around 16:00, sounds a bit strange to me that DK1 and DK2 were not delivered at the same time - or maybe some other factors that caused us getting prices at different time

Delete.

It could be the same for me:

  • 14:17:52: Not yet available.
  • 15:51:38: Available.

There were no retries in between because of the exponential back-off strategy. So most likely you got lucky and received prices between 14:17:52 and 15:00:00.

They are different services, so it’s hard to say. If NordPool had issues today, all services would be affected, but in different ways depending on their implementations (for example retry mechanisms and internal data processing).

1 Like