Nordpool integration - setup of sensor for Denmark with 3 tarif rates per day changing month by month

Hi

I am using the custom integration Nordpool (available in HACS) and have setup a sensor including all the tarifs, taxes etc

It is quite a challenge to keep up with as things change month by month. I want to share what I have done to try and make the installation flow

- platform: nordpool
  VAT: True
  price_in_cents: false
  low_price_cutoff: 0.9
  region: "DK2"
  precision: 2
  price_type: kWh
  additional_costs: >
      {% set transmission = [ 0.0725, 0.0725, 0.0725, 0.0725, 0.0725, 0.0725,
                              0.0725, 0.0725, 0.0725, 0.0725, 0.0725, 0.0725 ] %}
      {% set system       = [ 0.0675, 0.0675, 0.0675, 0.0675, 0.0675, 0.0675,
                              0.0675, 0.0675, 0.0675, 0.0675, 0.0675, 0.0675 ] %}
    {% set elafgift     = [ 0.0100, 0.0100, 0.0100, 0.0100, 0.0100, 0.0100,
                            0.8713, 0.8713, 0.8713, 0.8713, 0.8713, 0.8713 ] %}
    {% set netc_low     = [ 0.2126, 0.2296, 0.1886, 0.1886, 0.1886, 0.1886,
                            0.1886, 0.1886, 0.1886, 0.1886, 0.1886, 0.1886 ] %}
    {% set netc_high    = [ 0.6379, 0.6889, 0.5660, 0.2830, 0.2830, 0.2830,
                            0.2830, 0.2830, 0.2830, 0.5660, 0.5660, 0.5660 ] %}
    {% set netc_peak    = [ 1.9135, 2.0666, 1.6980, 0.7359, 0.7359, 0.7359,
                            0.7359, 0.7359, 0.7359, 1.6980, 1.6980, 1.6980 ] %}
    {% set provider = 0.057 %}
    {% set hour = now().hour %}
    {% set month = now().month - 1 %}
    {% set extras = transmission[month] + system[month] + elafgift[month] + provider %}
    {% if hour >=0 and hour < 6 %}
       {% set extras = extras+ netc_low[month] %}
    {% elif hour >=17 and hour < 21 %}
       {% set extras = extras + netc_peak[month] %}
    {% else %}
       {% set extras = extras + netc_high[month] %} 
    {% endif %}
    {{ extras | float }}

The primary purpose of this posting is to share the additional cost part

In my part of Denmark (Radius - greater Copenhagen) the tarifs are in 3 teers. Low, High, and Peak.
They are announced for the year, but reality is that they change every month at the moment.

Tarifs consists of several layers transmission and system tarifs seems to be stable within a year but this could change. The elafgift (tax) is lowered temporarily but increases again in July 2023 unless the politicians change their minds.

The Net tarif C is the one that really change. There was a change in January. Then again in February. And I just saw that it changes again in March (getting lower again). And there are planned changes in April and October for the seasons. So a simple formula is hopeless. And my if statements got more and more complicated. So I decided to define arrays for each tarif and tax with a price for each month. This makes it easier to update the sensor without having to program each time. You just update the matrixes as new prices come in and for the new year you just start at january again.

If the tarifs level change their time intervals then we have to code again.

Note one thing. The provider fee is a fixed value per kWh. Some providers have zero and some have high fees depending on your agreement. And many providers hide these fees.

The value I have put is one I have experimented myself to the value of. The 0.057 kr/kWh is what Andel Energi charges for a “Basis El med variabel timepris”. I found the value by calculating the difference between the price in their app and the price you get with the tarifs and taxes only.

Note that all prices in the matrix are including VAT (moms).

I hope this will be useful. I tried to put all this in a template variable and then just use that in the sensor definition but that does not work.

References:

The transmission tarif and system tarif are announced here (excl moms) Aktuelle tariffer

The transport tarif for Radius is here (you can turn on moms) Tariffer og netabonnement - Radius

And taxes for Denmark: https://fm.dk/media/26367/faktaark_lempelse-af-elafgift-til-minimumssats-i-seks-maaneder.pdf

Edits: Found a mistake - had used rounded off numbers for some tarifs

The eloverblik integration will have your tarif rates, so it will be easier to pull it from there.
And Radius will change the rates again at Marts 1st, so you will have to update your code soon anyway. :slight_smile:

I will have a look at that also. I have included the March update in my code I posted

I have tried the suggested sensor.

It fails to run when you restart Home Assistant and then loads when you manually reload template items.

It fails because the Nordpool entities are not yet active when it inits.

And if you reload the template sensor before 13:00 then it also fails because there is no tomorrow attribute

I tried to mend it a bit by putting some defaults in the floats but I still have this error

2023-01-19 12:27:50.256 ERROR (MainThread) [homeassistant.helpers.event] Error while processing template: Template("{% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and state_attr('sensor.nordpool_raw', 'tomorrow') %}

{% set ns = namespace (prices=[]) %}

{% for h in range(24) %}

{% set ns.prices = ns.prices + [( 1.25 * (float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h]) + float(state_attr('sensor.nordpool_raw', 'tomorrow')[h]) + 0.057 ) ) | round(2)] %}

{% endfor %}

{{ ns.prices }}

{% endif %}")

Traceback (most recent call last):

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1700, in forgiving_float

return float(value)

TypeError: float() argument must be a string or a real number, not 'NoneType'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 423, in async_render

render_result = _render_with_context(self.template, compiled, **kwargs)

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1950, in _render_with_context

return template.render(**kwargs)

File "/usr/local/lib/python3.10/site-packages/jinja2/environment.py", line 1301, in render

self.environment.handle_exception()

File "/usr/local/lib/python3.10/site-packages/jinja2/environment.py", line 936, in handle_exception

raise rewrite_traceback_stack(source=source)

File "<template>", line 4, in top-level template code

File "/usr/local/lib/python3.10/site-packages/jinja2/sandbox.py", line 393, in call

return __context.call(__obj, *args, **kwargs)

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1703, in forgiving_float

raise_no_default("float", value)

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1411, in raise_no_default

raise ValueError(

ValueError: Template error: float got invalid input 'None' when rendering template '{% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and state_attr('sensor.nordpool_raw', 'tomorrow') %}

{% set ns = namespace (prices=[]) %}

{% for h in range(24) %}

{% set ns.prices = ns.prices + [( 1.25 * (float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h]) + float(state_attr('sensor.nordpool_raw', 'tomorrow')[h]) + 0.057 ) ) | round(2)] %}

{% endfor %}

{{ ns.prices }}

{% endif %}' but no default was specified

The above exception was the direct cause of the following exception:

Traceback (most recent call last):

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 540, in async_render_to_info

render_info._result = self.async_render(variables, strict=strict, **kwargs)

File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 425, in async_render

raise TemplateError(err) from err

homeassistant.exceptions.TemplateError: ValueError: Template error: float got invalid input 'None' when rendering template '{% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and state_attr('sensor.nordpool_raw', 'tomorrow') %}

{% set ns = namespace (prices=[]) %}

{% for h in range(24) %}

{% set ns.prices = ns.prices + [( 1.25 * (float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h]) + float(state_attr('sensor.nordpool_raw', 'tomorrow')[h]) + 0.057 ) ) | round(2)] %}

{% endfor %}

{{ ns.prices }}

{% endif %}' but no default was specified

Small follow up.

The nordpool sensor raw has the attributes for today and tomorrow define as

 today: 0.62161, 0.61804, 0.61521, 0.63173, 0.69064, 0.78081, 0.87945, 1.11304, 1.18973, 1.1596, 1.15164, 1.03128, 1.0896, 1.12449, 1.15566, 1.2671, 1.34149, 1.29254, 1.16503, 1.12739, 1.0835, 1.0687, 0.88525, 0.7901
tomorrow: , , , , , , , , , , , , , , , , , , , , , , ,

So testing for its existance does not work. It returns None and that makes float barf

And then the tomorrow got valid numbers (it is past 13:00) and all works again. So this is a thing you need to test in the morning :wink:

Nordpool have an attribute called tomorrow_valid.
If it is true, then the tomorrow values are posted and when it is false the values in tomorrow will be none.

Some good and bad experiences

First the good. I changed the code so that it does not fail before 13:00.

The sensor.nordpool_raw is a Nordpool integration sensor without VAT.
And the sensor.eloverblik_tariff_sum is from the Eloverblik integration.

All is good until you try to make a sensor as a combination of them. The problem is that they are not in sync. And that creates glitches.

  - name: "Electricity Price Autotarif"
    unique_id: electricity_price_auto_tarif
    device_class: monetary
    unit_of_measurement: "DKK/kWh"
    state: >
      {{ ( 1.25 * ( float(states('sensor.eloverblik_tariff_sum'),0) + float(states('sensor.nordpool_raw'),0) ) + 0.057 ) | round(2) }}
    attributes:
      today: >
        {% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and state_attr('sensor.nordpool_raw', 'today') %}
          {% set ns = namespace (prices=[]) %}
          {% for h in range(24) %}
            {% set ns.prices = ns.prices + [( 1.25 * (float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h]) + float(state_attr('sensor.nordpool_raw', 'today')[h])) + 0.057 ) | round(2)] %}
          {% endfor %}
          {{ ns.prices }}
        {% endif %}
      tomorrow: >
        {% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and 
              state_attr('sensor.nordpool_raw', 'tomorrow_valid') and
              state_attr('sensor.nordpool_raw', 'tomorrow') %}
          {% set ns = namespace (prices=[]) %}
          {% for h in range(24) %}
            {% set ns.prices = ns.prices + [( 1.25 * (float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h]) + float(state_attr('sensor.nordpool_raw', 'tomorrow')[h]) + 0.057 ) ) | round(2)] %}
          {% endfor %}
          {{ ns.prices }}
        {% endif %}

image

The same situation using the sensor where I calculate the tarifs is clean

image

There are similar glithces happening sometimes when I restart HA. This is not a problem if you just want to display the time in the UI. The glitch is seconds.

But if you try to accumulate the product of consumption in the period and the price then the spikes create some false jumps.

Any good or bad experience - please share everyone. I will post more updates as I work on this.

If you want to avoid managing these tariffs for Danish areas, then the Energi Data Service integration in HACS can do automatic lookup for tariffs depending on region and net operator.

I passed that integration because the documentation of what it does and what it takes to use it is non-existing.

I will never for the world understand why people will spend days or weeks to code a piece of software but cannot be bothered to spend 1 hour writing a one page document of how to use it.

There is a wiki page - last updated mid 2022 and it says nothing about using the integration but instead has some developer geek info.

I tried to install this Energi Data Service.

It creates ONE entity with the current price and adds the tarifs

It talks about Carnot.dk where it seems I have to create an account. I decided to pass on that because i cannot find out what it does.

When you install it - you get the price but it still misses the markup from Andel Energi. So you can add this to the integration, right? Yes. When you add the integration you have a small text field where you can put a template for the additional cost. You need to know what to put there. It is not in the initial documentation. So I left it blank and continued the installation. Then I found that it is the same syntax as the Nordpool integration.

But - once added you cannot change it. There is no UI to change the additional cost.

You have to delete the integration and add it again. And you have to do that each and every time you want to alter this additional cost

With Nordpool it is the same problem. But at least with Nordpool, there is a documented way to add the integration with YAML.

But not for Energi Data Service. You can keep on adding and removing the integration each time you want to adjust the cost from Andel (which is not public information - you have to experiment with the value 3 defining decimals (0.00513 or so) until you get same price as the Andel Energi app shows. And they round off to 2 decimals so it takes time to get it consistant.

PLEASE - Malene - make this field possible to edit in the UI!
Or document a YAML alternative

Did you try to reconfigure the integration?
Then you can change the template.

No need to remove and readd to change anything.

And I do apologize that I’m just one woman on coding this, and that I have focus on making the code reliable rather than documentation.

And yes documentation WILL be made better, but the day only contains 24 hours :wink:

You are right. I looked the wrong place. And assumed it was as bad as the other energy integrations. Sorry about that. My bad!

And to anyone needing what next step is. Click configure.

When you submit you can select to automatically fetch tariffs and the next screen then asks for your energi company. For me it is Radius.

It really takes no time to document something like this. Just use screengrabbing and paste the images and you need very few words to finish it off. I just did it now and it took me 10 minutes to make this posting

In my screen shot you can see how you enter a fixed extra charge like the one most sellers charge and do not list anywhere. I wonder why they can get away with that

Check back on the documentation on the other side of the weekend.
Actually had planned on making documentation better (and up-to-date) during this weekend :wink:

1 Like

Nordpool seems pretty buggy with the initial autogenerated sensor.
You pretty much always have to make your own sensor by adding a config to configuration.yaml.

nordpool:

sensor:
  - platform: nordpool

    # Should the prices include vat? Default True
    VAT: True

    # What currency the api fetches the prices in
    # this is only need if you want a sensor in a non local currency
    currency: "DKK"

    # Option to show prices in cents (or the equivalent in local currency)
    price_in_cents: false

    # Helper so you can set your "low" price
    # low_price = hour_price < average * low_price_cutoff
    low_price_cutoff: 0.95

    # What power regions your are interested in.
    # Possible values: "DK1", "DK2", "FI", "LT", "LV", "Oslo", "Kr.sand", "Bergen", "Molde", "Tr.heim", "Tromsø", "SE1", "SE2", "SE3","SE4", "SYS", "EE"
    region: "DK2"

    # How many decimals to use in the display of the price
    precision: 2

    # What the price should be displayed in default
    # Possible values: MWh, kWh and Wh
    # default: kWh
    price_type: kWh

    # This option allows the usage of a template to add a tariff.
    # now() always refers start of the hour of that price.
    # this way we can calculate the correct costs add that to graphs etc.
    # The price result of the additional_costs template expects this additional cost to be in kWh and not cents as a float
    # default {{0.0|float}}
    additional_costs: "{{0.0|float}}"

template:
  - sensor:
      - name: "Electricity Cost"
        unique_id: electricity_cost
        device_class: monetary
        unit_of_measurement: "kr/kWh"
        state: >
          {{ (1.25 * float(states('sensor.eloverblik_tariff_sum'),0.0)) + (float(states('sensor.nordpool_kwh_dk2_dkk_4_10_025'),0.0)/100) }}
        attributes:
          today: >
            {% if state_attr('sensor.eloverblik_tariff_sum', 'hourly') and state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025', 'today') %}
              {% set ns = namespace (prices=[]) %}
              {% for h in range(24) %}
                {% set price_element={'start':(state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025','raw_today')[h].start.strftime('%Y-%m-%dT%H:%M:%S%z'))|string,'value':(1.25 * float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h],0.0) + (float(state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025', 'today')[h],0.0))/100) | round(5)} %}
                {% set ns.prices = ns.prices + [price_element] %}
              {% endfor %}
              {{ ns.prices }}
            {% endif %}
          tomorrow: >
            {% if state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025', 'tomorrow_valid') %}
              {% set ns = namespace (prices=[]) %}
              {% for h in range(24) %}
                {% set price_element={'start':(state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025','raw_tomorrow')[h].start.strftime('%Y-%m-%dT%H:%M:%S%z'))|string,'value':(1.25 * float(state_attr('sensor.eloverblik_tariff_sum', 'hourly')[h],0.0) + (float(state_attr('sensor.nordpool_kwh_dk2_dkk_4_10_025', 'tomorrow')[h],0.0))/100) | round(5)} %}
                {% set ns.prices = ns.prices + [price_element] %}
              {% endfor %}
              {{ ns.prices }}
            {% endif %}

One last followup on the Energy Data Service integration.

In the example I chose to see the price incl VAT. Like most non-business users I guess.

The integration had VAT to the additional costs so it must be entered without VAT. So when you first put 0.0 and compare the result with the price in whatever app or website your energy seller has, you need to multiply the difference by 1/VAT (0.8 in Denmark) to get the additional without VAT. So in my case the amount to Andel Energi is 0.0456 DKK. And that number is probably not 100% correct but very near.

I am actually testing out multiple solutions at the moment and fighting different problems. Self-inflicted to some degree because I did not think carefully about race conditions.

First
I have a Kamstrup meter with a real electrical interface running AmsToMqttBridge. This both gives me accurate data directly from the meter and it actually also has a feature to fetch data on the prices. It does that from ENTSO-E (need token for that). And then I have to add the tariffs, tax, VAT etc to that.
The meter sends wonderful data for consumption per hour, per day, per month, actual meter reading. All wonderful.

Until you want to calculate the accumulated price per hour, per month etc. I do not want to do Rieman integration of power. I want to use the energy data from the meter because at the end of the day, this is what I am charged for and I know i will accumulate significant error if I do power integrated over time.

I naively created a template sensor that calculates the product of the hour consumption by the price of that hour.

Problem is that the change of price and the reset of the reported consumption does not happen “atomically”. There are sometimes seconds between the two. And that you see a glitch in the calculation where the new price is multiplied by the data from the previous hour. Especially the price change from the meter comes very late - several seconds - sometimes - at it is reported by MQTT from an ESP8266 that runs with some power saving also.

So I have to make this differently. Some fun for the weekend.

The reason I experiement with different sources of prices and tariffs is that this data needs to be stable and not have glitches either. The best combo yet is Nordpool with my own tariffs added as I originally posted. I have yet to see how stable the data from Energy Data Service is as I just added that to HA

One more follow up.

My original goal was to first create a sensor that represents the current price of the hour.

And then create sensors that show cost in DKK per hour, week, month etc

As I wrote earlier the challenge is to get things in sync. And the solution is to not have anything depending on being in sync

When you have the actual absolute reading of the meter then you simply multiply the current meter with current price. That gives a huge amount. But then you feed that to a utility_meter sensor (helpers) and it will keep on adding up the delta for each reading and you actually get the cost summarized

Here is the sensor

  - name: "Electricity Spend"
    unique_id: electricity_spend
    device_class: monetary
    unit_of_measurement: "DKK"
    state: >
      {{ float(states('sensor.nordpool_electricity_price'),0) * float(states('sensor.kamstrup_tpi'),0)}}

Do not round off this sensor as that will accumulate large errors

And you then make a utility_meter in the GUI for helpers and make it tick on sensor.electricity_spend

And you can make one for hour, one for day, one for week etc,

The only thing I have not figured out is if you can hack the value to a start value. I know where my monthly and yearly are based on an IOS app. Not so important. The most important is the fun of being able to see what the last days have cost not only in kWh but also in money. The apps are always days behind.

EDIT. This is rubbish. The way utility_meter wirks makes this go crazy when prices increase. It only works when prices decrease. I need to base it on the actual immediate cost per update of sensors. The problem of that is to avoid glitches incl restart of HA

And calibrating a utility meter to a value to continue from - easy. There is a service for it you just run from developer tools

service: utility_meter.calibrate
data:
  value: 487
target:
  entity_id: sensor.electricity_spend_yearly

That was easier than I thought.