Any good ideas are welcome. Nordpool Energy Price per hour

Wanted a breakdown showing that the Government gets most of the money, took some fiddling around but I’m happy with the result.

type: custom:apexcharts-card
graph_span: 48h
stacked: true
header:
  title: Energy price breakdown
  show: true
span:
  start: day
now:
  show: true
  label: Now
apex_config:
  legend:
    show: true
series:
  - entity: sensor.nordpool_kwh_se4_sek_3_10_025
    name: tax
    type: column
    float_precision: 2
    extend_to: false
    color: red
    unit: ' '
    show:
      legend_value: false
    data_generator: >
      return
      entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).map((start,
      index) => {
        return [new Date(start["start"]).getTime(), Math.min(0, entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow)[index]["value"]) * 0.8 + 0.392];
      });
  - entity: sensor.nordpool_kwh_se4_sek_3_10_025
    name: transfer
    type: column
    float_precision: 2
    extend_to: false
    color: orange
    unit: ' '
    show:
      legend_value: false
    data_generator: >
      return
      entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).map((start,
      index) => {
        return [new Date(start["start"]).getTime(), 0.1104];
      });
  - entity: sensor.nordpool_kwh_se4_sek_3_10_025
    name: E.ON add
    type: column
    float_precision: 2
    extend_to: false
    color: yellow
    unit: ' '
    show:
      legend_value: false
    data_generator: >
      return
      entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).map((start,
      index) => {
        return [new Date(start["start"]).getTime(), 0.1041];
      });
  - entity: sensor.nordpool_kwh_se4_sek_3_10_025
    name: VAT
    type: column
    float_precision: 2
    extend_to: false
    color: blue
    unit: ' '
    show:
      legend_value: false
    data_generator: >
      return
      entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).map((start,
      index) => {
        return [new Date(start["start"]).getTime(), entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow)[index]["value"] *0.2 + 0.151625 ];
      });
  - entity: sensor.nordpool_kwh_se4_sek_3_10_025
    name: spot
    type: column
    float_precision: 2
    extend_to: false
    color: green
    unit: ' '
    show:
      legend_value: false
    data_generator: >
      return
      entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).map((start,
      index) => {
        return [new Date(start["start"]).getTime(), entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow)[index]["value"] * 0.8];
      });
yaxis:
  - min: ~0
    max: ~2

This is my very first post here, so please be kind if I did something wrong :wink:

Best regards,

6 Likes

I just combined the raw data for today and tomorrow and display it with apexchart:

2023-09-25_13-02-16

Just want to thank all of you for sharing code. Its priceless for us who only have a master’s degree in Cut and Paste.

My thermostat uses Nordpool for two saving modes (color blue price under 0, green price up to 0,8 NOK, etc. Black color tomorrow):

  1. Mode Cost: I can set a cost value (slider) and then the thermostat is on when cost is like or under slider value.
    The normal schedule day/weekend schedule works except that heat is not adjusted down at “Day heat down” hours.
  2. I can set a number of hours in a day, so the thermostat only heats (temp. normal) these hours, and the” temp.min” for the rest of the day.

Normal schedule day/weekend schedule - follows the time settings. In the weekends and national holidays, the heat is not adjusted down at “Day heat down” hours. When house is in Holliday- or Away-mode, temp. min is set 24-hours.

4 Likes

Hey! I want a sensor showing my the cheapest (or most expensive) (lets say) 3 hours of energy today.
I have one that I got from someon else online that shows exactly this but tomorrow.
Is there a way to change this to show the 3 cheapest (and/or most expensive) hours today? Or is there some easier way to make a template-sensor showing this?

- sensor:
    - name: XXXX
      unique_id: XXXX
      state: >
        {%- set numberOfSequentialHours =  states('input_number.elprisverktyg_antal_timmar') | int -%}
        {%- set lastHour = states('input_number.elprisverktyg_last_hour') | int -%}
        {%- set firstHour = states('input_number.elprisverktyg_first_hour') | int -%}
​
        {%- if state_attr('sensor.nordpool_kwh_se3_sek_3_10_025_med_moms', 'tomorrow_valid') == true -%}
          {%- set ns = namespace(counter=0, list=[], cheapestHour=today_at("00:00") + timedelta( hours = (24)), cheapestPrice=999.00) -%}
          {%- for i in range(firstHour + numberOfSequentialHours, lastHour+1) -%}
            {%- set ns.counter = 0.0 -%}
            {%- for j in range(i-numberOfSequentialHours, i) -%}
              {%- set ns.counter = ns.counter + state_attr('sensor.nordpool_kwh_se3_sek_3_10_025_med_moms', 'tomorrow')[j] -%}
            {%- endfor -%}
            {%- set ns.list = ns.list + [ns.counter] -%}
            {%- if ns.counter < ns.cheapestPrice -%}
              {%- set ns.cheapestPrice = ns.counter -%}
              {%- set ns.cheapestHour = today_at("00:00") + timedelta( hours = (24 + i - numberOfSequentialHours)) -%}
            {%- endif -%}
          {%- endfor -%}
          {{ ns.cheapestHour }}
          {%- set ns.cheapestPrice = ns.cheapestPrice / numberOfSequentialHours -%}
        {%- endif -%}

Here is a working code for the one you found and pasted in your post. Just change “sensor.nordpool” to your own one

- sensor:
    - name: Daily Energy Price Status
      unique_id: XXXX
      state: >
        {% set min_price = state_attr('sensor.nordpool', 'min')  | float %}
        {% set max_price = state_attr('sensor.nordpool', 'max')  | float %}
        {% set current_price = state_attr('sensor.nordpool', 'current_price')  | float %}
        {% set price_quantiles = {0: 'Very Cheap 1/5', 1: 'Cheap 2/5', 2: 'In Between 3/5', 3: 'Expensive 4/5', 4: 'Very Expensive 5/5'} %}
        {% if current_price < min_price%}
          Very Cheap
        {% elif current_price > max_price %}
          Very Expensive
        {% else %}
          {{ price_quantiles[(current_price - min_price) / (max_price - min_price) // 0.2] }}
        {% endif %}
1 Like

Yes, here is some code that does that and more. Every line that starts with " - type: markdown" is a new window with new info. Just change “sensor.nordpool” to your own one

type: vertical-stack
cards:
  - type: markdown
    alignment: justify
    title: null
    content: |-
      Nu: {{ '%.2f' | format((state_attr('sensor.nordpool',
           'current_price')|float / 0.05) | round() * 0.05) }}
       Average: {{ '%.2f' | format((state_attr('sensor.nordpool',
           'average')|float / 0.05) | round() * 0.05) }}  
       Median: {{ '%.2f' | format((state_attr('sensor.nordpool',
           'mean')|float / 0.05) | round() * 0.05) }}  
       Max: {{ '%.2f' | format((state_attr('sensor.nordpool',
           'max')|float / 0.05) | round() * 0.05) }}  
       Min: {{ '%.2f' | format((state_attr('sensor.nordpool',
           'min')|float / 0.05) | round() * 0.05) }}
       
       {% if (now().strftime('%T')) > '13:01:00'  %}
       Imorgon:
       Average: {{state_attr('sensor.nordpool', 
       'tomorrow')|average|round(1)}}
       Min: {{state_attr('sensor.nordpool', 
       'tomorrow')|min|round(1)}}
       Max: {{state_attr('sensor.nordpool',
       'tomorrow')|max|round(1)}}

       {%- else %}
       Elpriserna för imorgon är inte tillgängliga förrän kl.13           
       {% endif -%}

  - type: markdown
    alignment: justify
    content: >-
      {% set priceList = state_attr('sensor.nordpool', 'today')[00:23] %} {% set
      dateList = state_attr('sensor.nordpool', 'raw_today')[00:23] %} {% set
      minPrice = min(priceList) %} {% set minIndex = priceList.index(minPrice)
      %} {% set minDateTimeStr= dateList[minIndex].start | string %} {% set
      minDateTime= strptime(minDateTimeStr[0:19], '%Y-%m-%d %H:%M:%S') %}
      Billigaste priset idag är {{ minPrice }} Öre/kWh och startar kl {{
      minDateTime.hour }}:00

  - type: markdown
    alignment: justify
    content: "{% set iterativesum = namespace(iter=[]) %}\n      {% set lowestiter = namespace(kr=2) %}\n      {% set timelowest = namespace(hr=2) %}\n      {% set highestiter = namespace(kr=0) %}\n      {% set timehighest = namespace(hr=0) %}\n      {% set num_hours = 4 | int %}\n      {% set nordpoolentity = 'sensor.nordpool' %}\n      {% set timemapper = { \n      0: '00:00', 1 : '01:00', 2 : '02:00', 3 : '03:00',\n      4 : '04:00', 5 : '05:00', 6 : '06:00', 7 : '07:00',\n      8 : '08:00', 9 : '09:00', 10 : '10:00', 11 : '11:00',\n      12 : '12:00', 13 : '13:00', 14 : '14:00', 15 : '15:00',\n      16 : '16:00', 17 : '17:00', 18 : '18:00', 19 : '19:00',\n      20 : '20:00', 21 : '21:00', 22 : '22:00', 23 : '23:00',\n      24 : '00:00', 25 : '01:00', 26 : '02:00', 27 : '03:00',\n      28 : '04:00', 29 : '05:00', 30 : '06:00', 31 : '07:00',\n      32 : '08:00', 33 : '09:00', 34 : '10:00', 35 : '11:00',\n      36 : '12:00', 37 : '13:00', 38 : '14:00', 39 : '15:00',\n      40 : '16:00', 41 : '17:00', 42 : '18:00', 43 : '19:00',\n      44 : '20:00', 45 : '21:00', 46 : '22:00', 47 : '23:00',\n      48 : '0:00', } %}\n      {% set prices = namespace(price=[]) %}\n      {% set prices.price = prices.price + state_attr(nordpoolentity, 'today') %}\n      {%- if state_attr(nordpoolentity,'tomorrow') | length == 1 -%}\n      Morgondagens priser ej släppta\n      {% else %}\n      {% set prices.price = prices.price + state_attr(nordpoolentity,\t'tomorrow') %}\n      {% endif %}\n      {%- for n in range(prices.price|length -num_hours +1) -%}\n      {%- set tempsum= namespace(temp=0) -%}\n      {%- for i in range(num_hours) -%}\n      {%- set tempsum.temp = tempsum.temp + prices.price[n+i] -%}\n      {% endfor -%}\n      {% set iterativesum.iter = iterativesum.iter + [tempsum.temp] -%}\n      {% endfor -%}\n      {% for iter in iterativesum.iter -%}\n      {%- if loop.index > now().hour -%}\n      {%- if iter < lowestiter.kr | float -%}\n      {%- set lowestiter.kr = iter | float -%}\n      {%- set timelowest.hr = loop.index -1 -%}\n      {%- endif -%}\n      {%- if iter > highestiter.kr | float -%}\n      {%- set highestiter.kr = iter | float -%}\n      {%- set timehighest.hr = loop.index -1 -%}\n      {%- endif -%}\n      {%- endif -%}\n      {% endfor -%}\n      De billigaste fyra ({{num_hours}}) timmarna startar {% if (timelowest.hr < 24) %}idag{% else %}imorgon{% endif %} kl {{timemapper[timelowest.hr]}}, då snittpriset är {{\"%.2f\"|format(lowestiter.kr/num_hours)}} Öre/kWh. "

  - type: markdown
    alignment: justify
    content: "{% set iterativesum = namespace(iter=[]) %}\n      {% set lowestiter = namespace(kr=2) %}\n      {% set timelowest = namespace(hr=2) %}\n      {% set highestiter = namespace(kr=0) %}\n      {% set timehighest = namespace(hr=0) %}\n      {% set num_hours = 4 | int %}\n      {% set nordpoolentity = 'sensor.nordpool' %}\n      {% set timemapper = { \n      0: '00:00', 1 : '01:00', 2 : '02:00', 3 : '03:00',\n      4 : '04:00', 5 : '05:00', 6 : '06:00', 7 : '07:00',\n      8 : '08:00', 9 : '09:00', 10 : '10:00', 11 : '11:00',\n      12 : '12:00', 13 : '13:00', 14 : '14:00', 15 : '15:00',\n      16 : '16:00', 17 : '17:00', 18 : '18:00', 19 : '19:00',\n      20 : '20:00', 21 : '21:00', 22 : '22:00', 23 : '23:00',\n      24 : '00:00', 25 : '01:00', 26 : '02:00', 27 : '03:00',\n      28 : '04:00', 29 : '05:00', 30 : '06:00', 31 : '07:00',\n      32 : '08:00', 33 : '09:00', 34 : '10:00', 35 : '11:00',\n      36 : '12:00', 37 : '13:00', 38 : '14:00', 39 : '15:00',\n      40 : '16:00', 41 : '17:00', 42 : '18:00', 43 : '19:00',\n      44 : '20:00', 45 : '21:00', 46 : '22:00', 47 : '23:00',\n      48 : '0:00', } %}\n      {% set prices = namespace(price=[]) %}\n      {% set prices.price = prices.price + state_attr(nordpoolentity, 'today') %}\n      {%- if state_attr(nordpoolentity,'tomorrow') | length == 1 -%}\n      Morgondagens priser ej släppta\n      {% else %}\n      {% set prices.price = prices.price + state_attr(nordpoolentity,\t'tomorrow') %}\n      {% endif %}\n      {%- for n in range(prices.price|length -num_hours +1) -%}\n      {%- set tempsum= namespace(temp=0) -%}\n      {%- for i in range(num_hours) -%}\n      {%- set tempsum.temp = tempsum.temp + prices.price[n+i] -%}\n      {% endfor -%}\n      {% set iterativesum.iter = iterativesum.iter + [tempsum.temp] -%}\n      {% endfor -%}\n      {% for iter in iterativesum.iter -%}\n      {%- if loop.index > now().hour -%}\n      {%- if iter < lowestiter.kr | float -%}\n      {%- set lowestiter.kr = iter | float -%}\n      {%- set timelowest.hr = loop.index -1 -%}\n      {%- endif -%}\n      {%- if iter > highestiter.kr | float -%}\n      {%- set highestiter.kr = iter | float -%}\n      {%- set timehighest.hr = loop.index -1 -%}\n      {%- endif -%}\n      {%- endif -%}\n      {% endfor -%}\n      De dyraste fyra ({{num_hours}}) timmarna startar {% if (timehighest.hr < 24) %}idag{% else %}imorgon{% endif %} kl {{timemapper[timehighest.hr]}}, då snittpriset är {{\"%.2f\"|format(highestiter.kr/num_hours)}} Öre/kWh"

  - type: markdown
    alignment: justify
    content: >-
      {% set priceList = state_attr('sensor.nordpool', 'tomorrow')[00:23] %} {%
      set dateList = state_attr('sensor.nordpool', 'raw_tomorrow')[00:23] %} {%
      set minPrice = min(priceList) %} {% set minIndex =
      priceList.index(minPrice) %} {% set minDateTimeStr=
      dateList[minIndex].start | string %} {% set minDateTime =
      strptime(minDateTimeStr[0:19], '%Y-%m-%d %H:%M:%S') %} Billigaste priset
      imorgon är {{ minPrice }} Öre/kWh och startar kl {{ minDateTime.hour }}:00

2 Likes

Wow thank you! This was what I was looking for! :slight_smile:

Wanna share the code?

Thanks @Tibbie86 , great work. I added those two codes together and did some minor adjustments:

  1. Translated to english and used €
  2. If the time has passed it will say like this: The cheapest price today started at 4:00 and was 0.093 €/kWh.
  3. Added a number selector so you can change how many cheapest hours you want to see. Made number helper. In my case input_number.cheapest_hours_needed and changed your code, that will use it.

Result:

Lovelace Code. Note that I used custom card: numberbox-card:

type: vertical-stack
cards:
  - type: markdown
    alignment: justify
    content: >-
      {% set min_price = state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'min')
      | float %} {% set max_price =
      state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'max') | float %} {% set
      current_price = state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
      'current_price') | float %} {% set price_quantiles = {0: 'Price is
      currently Very Cheap 1/5', 1: 'Price is currently Cheap 2/5', 2: 'Price is
      currently In Between 3/5', 3: 'Price is currently Expensive 4/5', 4:
      'Price is currently Very Expensive 5/5'} %} {% if current_price <
      min_price %}
        Very Cheap
      {% elif current_price > max_price %}
        Very Expensive
      {% else %}
        {{ price_quantiles[(current_price - min_price) / (max_price - min_price) // 0.2] }}
      {% endif %}
  - type: markdown
    alignment: justify
    content: >-
      {% set priceList = state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
      'today')[00:23] %} {% set dateList =
      state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'raw_today')[00:23] %} {%
      set minPrice = min(priceList) %} {% set minIndex =
      priceList.index(minPrice) %} {% set minDateTimeStr =
      dateList[minIndex].start | string %} {% set minDateTime =
      strptime(minDateTimeStr[0:19], '%Y-%m-%d %H:%M:%S') %} {% set currentHour
      = now().hour %} {% if currentHour < minDateTime.hour %}
        The cheapest price today is {{ minPrice }} €/kWh and starts at {{ minDateTime.hour }}:00.
      {% else %}
        The cheapest price today started at {{ minDateTime.hour }}:00 and was {{ minPrice }} €/kWh.
      {% endif %}
  - type: custom:numberbox-card
    border: true
    entity: input_number.cheapest_hours_needed
  - type: markdown
    alignment: justify
    content: >-
      {% set iterativesum = namespace(iter=[]) %}
      {% set lowestiter = namespace(kr=2) %}
      {% set timelowest = namespace(hr=2) %}
      {% set highestiter = namespace(kr=0) %}
      {% set timehighest = namespace(hr=0) %}
      {% set num_hours = states('input_number.cheapest_hours_needed') | int %}
      {% set nordpoolentity = 'sensor.nordpool_kwh_ee_eur_3_10_0' %}
      {% set timemapper = {  0: '00:00', 1 : '01:00', 2 : '02:00', 3 : '03:00', 4 : '04:00', 5 : '05:00', 6 : '06:00', 7 : '07:00', 8 : '08:00', 9 : '09:00', 10 : '10:00', 11 : '11:00', 12 : '12:00', 13 : '13:00', 14 : '14:00', 15 : '15:00', 16 : '16:00', 17 : '17:00', 18 : '18:00', 19 : '19:00', 20 : '20:00', 21 : '21:00', 22 : '22:00', 23 : '23:00', 24 : '00:00', 25 : '01:00', 26 : '02:00', 27 : '03:00', 28 : '04:00', 29 : '05:00', 30 : '06:00', 31 : '07:00', 32 : '08:00', 33 : '09:00', 34 : '10:00', 35 : '11:00', 36 : '12:00', 37 : '13:00', 38 : '14:00', 39 : '15:00', 40 : '16:00', 41 : '17:00', 42 : '18:00', 43 : '19:00', 44 : '20:00', 45 : '21:00', 46 : '22:00', 47 : '23:00', 48 : '0:00', } %}
      {% set prices = namespace(price=[]) %}
      {% set prices.price = prices.price + state_attr(nordpoolentity, 'today') %}
      {%- if state_attr(nordpoolentity,'tomorrow') | length == 1 -%}
      Electricity prices for tomorrow are not available. 
      {%- else %}
      {% set prices.price = prices.price + state_attr(nordpoolentity, 'tomorrow') %}
      {%- endif %}
      {%- for n in range(prices.price|length - num_hours + 1) -%}
      {%- set tempsum = namespace(temp=0) -%}
      {%- for i in range(num_hours) -%}
      {%- set tempsum.temp = tempsum.temp + prices.price[n+i] -%}
      {%- endfor -%}
      {% set iterativesum.iter = iterativesum.iter + [tempsum.temp] -%}
      {%- endfor -%}
      {% for iter in iterativesum.iter -%}
      {%- if loop.index > now().hour -%}
      {%- if iter < lowestiter.kr | float -%}
      {%- set lowestiter.kr = iter | float -%}
      {%- set timelowest.hr = loop.index -1 -%}
      {%- endif -%}
      {%- if iter > highestiter.kr | float -%}
      {%- set highestiter.kr = iter | float -%}
      {%- set timehighest.hr = loop.index -1 -%}
      {%- endif -%}
      {%- endif -%}
      {%- endfor -%}
      The cheapest {{ num_hours }} {{ 'hour' if num_hours == 1 else 'hours' }} start {% if (timelowest.hr < 24) %}today{% else %}tomorrow{% endif %} at {{ timemapper[timelowest.hr] }}, with an average price of {{ "%.2f"|format(lowestiter.kr/num_hours) }} €/kWh.

  - type: markdown
    alignment: justify
    title: null
    content: >-
      Now: {{ '%.2f' | format((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
           'current_price')|float / 0.05) | round() * 0.05) }}
      Average: {{ '%.2f' |
      format((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
           'average')|float / 0.05) | round() * 0.05) }}  
      Median: {{ '%.2f' |
      format((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
           'mean')|float / 0.05) | round() * 0.05) }}  
      Max: {{ '%.2f' | format((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
           'max')|float / 0.05) | round() * 0.05) }}  
      Min: {{ '%.2f' | format((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',
           'min')|float / 0.05) | round() * 0.05) }}
       
      {% if (now().strftime('%T')) > '13:01:00'  %}

      Tomorrow:

      Average: {{state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 

      'tomorrow')|average|round(1)}}

      Min: {{state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 

      'tomorrow')|min|round(1)}}

      Max: {{state_attr('sensor.nordpool_kwh_ee_eur_3_10_0',

      'tomorrow')|max|round(1)}}


      {%- else %}

      Electricity prices for tomorrow are not available until 13:00.

      {% endif -%}

1 Like

Did you have any luck solving it? I’m trying to solve the same problem, but really stuck.

Now I want to find a way how to calculate the money i have earned from selling the energy to grid (Nordpool without VAT) and how much i have saved using the solar energy (nordpool with VAT + tariffs). Probably something similar to the Energy Dashboard logic.

So what i have:

Prices.

  1. Nordpool hourly price (current price) excluding VAT: sensor.nordpool_kwh_ee_eur_3_10_0
  2. Nordpool hourly price (current price) including VAT: sensor.nordpool_kwh_ee_eur_3_05_02
  3. Network fee, or Tariff: sensor.el_tariff
    *Actually i have the sensor.total_el_price, which sums the 1. and 3. and which i’m using in Energy Dashboard

Returned electricity to the grid

  1. Phase 1: sensor.phase_1_energy_returned
  2. Phase 2: sensor.phase_2_energy_returned
  3. Phase 3: sensor.phase_3_energy_returned

PV production:
Only one sensor that sums the three Phases: sensor.inverter_total_yield. Usually it produces equal amount on all three phases.

I have managed to make template sensors to calculate “sold to grid” and “used by myself” energy:

  - platform: template
    sensors:
      sold_to_grid_energy:
        friendly_name: "Sold to Grid energy"
        unit_of_measurement: "kWh"
        value_template: >-
          {{ (states('sensor.phase_1_energy_returned') | float + 
               states('sensor.phase_2_energy_returned') | float + 
               states('sensor.phase_3_energy_returned') | float) }}
  - platform: template
    sensors:
      used_self_pv_energy:
        friendly_name: "Used Self PV Energy"
        unit_of_measurement: "kWh"
        value_template: >-
          {% set pv_production = states('sensor.inverter_total_yield') | float %}
          {% set energy_returned = states('sensor.phase_1_energy_returned') | float + 
                                   states('sensor.phase_2_energy_returned') | float + 
                                   states('sensor.phase_3_energy_returned') | float %}
          {% set used_self_pv = pv_production - energy_returned %}
          {{ used_self_pv }}

Now next step would be to calculate the costs based on usage during specific hours and then cumulatively. This actually excaclty what Energy Dashboard is doing, and actually I have at least the energy sold numbers from there, but not used PV energy numbers and because of that i would like to replicate it outside of Energy Dashboard. Seems like a simple task as it has been already done in Energy Dasboard, but i’m out of ideas how to move on.


Would be extremely nice if Energy Dashboard could calculate this out of the box.

In the end I would like to see:

  1. Daily PV energy sold - kwh and €
  2. Daily PV energy self used - kwh and €
  3. Daily total PV earning - kwh and €
  4. Monhtly PV energy sold - kwh and €
  5. Monhtly PV energy self used - kwh and €
  6. Monthly total PV earning - kwh and €
  7. Yearly PV energy sold - kwh and €
  8. Yearly PV energy self used - kwh and €
  9. Yearly total PV earning - kwh and €
  10. All Time PV energy sold - kwh and €
  11. All Time PV energy self used - kwh and €
  12. All Time total PV earning - kwh and €

Hello arva

so i tried your method but got stuck here

Missing:‘input_number.cheapest_hours_needed’

and the bar of where it says the cheapest 4 hours start

is empty. i only changed the lines with updated version
nordpool_kwh_nl_eur_3_10_021.

type: vertical-stack
cards:

  • type: markdown
    alignment: justify
    content: >-
    {% set min_price = state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘min’) | float %} {% set max_price =
    state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’, ‘max’) | float %} {% set
    current_price = state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘current_price’) | float %} {% set price_quantiles = {0: ‘Price is
    currently Very Cheap 1/5’, 1: ‘Price is currently Cheap 2/5’, 2: ‘Price is
    currently In Between 3/5’, 3: ‘Price is currently Expensive 4/5’, 4:
    ‘Price is currently Very Expensive 5/5’} %} {% if current_price <
    min_price %}
    Very Cheap
    {% elif current_price > max_price %}
    Very Expensive
    {% else %}
    {{ price_quantiles[(current_price - min_price) / (max_price - min_price) // 0.2] }}
    {% endif %}

  • type: markdown
    alignment: justify
    content: >-
    {% set priceList = state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘today’)[00:23] %} {% set dateList =
    state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’, ‘raw_today’)[00:23] %}
    {% set minPrice = min(priceList) %} {% set minIndex =
    priceList.index(minPrice) %} {% set minDateTimeStr =
    dateList[minIndex].start | string %} {% set minDateTime =
    strptime(minDateTimeStr[0:19], ‘%Y-%m-%d %H:%M:%S’) %} {% set currentHour
    = now().hour %} {% if currentHour < minDateTime.hour %}
    The cheapest price today is {{ minPrice }} €/kWh and starts at {{ minDateTime.hour }}:00.
    {% else %}
    The cheapest price today started at {{ minDateTime.hour }}:00 and was {{ minPrice }} €/kWh.
    {% endif %}

  • type: custom:numberbox-card
    border: true
    entity: input_number.cheapest_hours_needed

  • type: markdown
    alignment: justify
    content: >-
    {% set iterativesum = namespace(iter=) %} {% set lowestiter =
    namespace(kr=2) %} {% set timelowest = namespace(hr=2) %} {% set
    highestiter = namespace(kr=0) %} {% set timehighest = namespace(hr=0) %}
    {% set num_hours = states(‘input_number.cheapest_hours_needed’) | int %}
    {% set nordpoolentity = ‘sensor.nordpool_kwh_nl_eur_3_10_021’ %} {% set
    timemapper = { 0: ‘00:00’, 1 : ‘01:00’, 2 : ‘02:00’, 3 : ‘03:00’, 4 :
    ‘04:00’, 5 : ‘05:00’, 6 : ‘06:00’, 7 : ‘07:00’, 8 : ‘08:00’, 9 : ‘09:00’,
    10 : ‘10:00’, 11 : ‘11:00’, 12 : ‘12:00’, 13 : ‘13:00’, 14 : ‘14:00’, 15 :
    ‘15:00’, 16 : ‘16:00’, 17 : ‘17:00’, 18 : ‘18:00’, 19 : ‘19:00’, 20 :
    ‘20:00’, 21 : ‘21:00’, 22 : ‘22:00’, 23 : ‘23:00’, 24 : ‘00:00’, 25 :
    ‘01:00’, 26 : ‘02:00’, 27 : ‘03:00’, 28 : ‘04:00’, 29 : ‘05:00’, 30 :
    ‘06:00’, 31 : ‘07:00’, 32 : ‘08:00’, 33 : ‘09:00’, 34 : ‘10:00’, 35 :
    ‘11:00’, 36 : ‘12:00’, 37 : ‘13:00’, 38 : ‘14:00’, 39 : ‘15:00’, 40 :
    ‘16:00’, 41 : ‘17:00’, 42 : ‘18:00’, 43 : ‘19:00’, 44 : ‘20:00’, 45 :
    ‘21:00’, 46 : ‘22:00’, 47 : ‘23:00’, 48 : ‘0:00’, } %} {% set prices =
    namespace(price=) %} {% set prices.price = prices.price +
    state_attr(nordpoolentity, ‘today’) %} {%- if
    state_attr(nordpoolentity,‘tomorrow’) | length == 1 -%} Electricity prices
    for tomorrow are not available. {%- else %} {% set prices.price =
    prices.price + state_attr(nordpoolentity, ‘tomorrow’) %} {%- endif %} {%-
    for n in range(prices.price|length - num_hours + 1) -%} {%- set tempsum =
    namespace(temp=0) -%} {%- for i in range(num_hours) -%} {%- set
    tempsum.temp = tempsum.temp + prices.price[n+i] -%} {%- endfor -%} {% set
    iterativesum.iter = iterativesum.iter + [tempsum.temp] -%} {%- endfor -%}
    {% for iter in iterativesum.iter -%} {%- if loop.index > now().hour -%}
    {%- if iter < lowestiter.kr | float -%} {%- set lowestiter.kr = iter |
    float -%} {%- set timelowest.hr = loop.index -1 -%} {%- endif -%} {%- if
    iter > highestiter.kr | float -%} {%- set highestiter.kr = iter | float
    -%} {%- set timehighest.hr = loop.index -1 -%} {%- endif -%} {%- endif -%}
    {%- endfor -%} The cheapest {{ num_hours }} {{ ‘hour’ if num_hours == 1
    else ‘hours’ }} start {% if (timelowest.hr < 24) %}today{% else
    %}tomorrow{% endif %} at {{ timemapper[timelowest.hr] }}, with an average
    price of {{ “%.2f”|format(lowestiter.kr/num_hours) }} €/kWh.

  • type: markdown
    alignment: justify
    title: null
    content: >-
    Now: {{ ‘%.2f’ | format((state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘current_price’)|float / 0.05) | round() * 0.05) }}
    Average: {{ ‘%.2f’ |
    format((state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘average’)|float / 0.05) | round() * 0.05) }}
    Median: {{ ‘%.2f’ |
    format((state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘mean’)|float / 0.05) | round() * 0.05) }}
    Max: {{ ‘%.2f’ | format((state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘max’)|float / 0.05) | round() * 0.05) }}
    Min: {{ ‘%.2f’ | format((state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,
    ‘min’)|float / 0.05) | round() * 0.05) }}

    {% if (now().strftime(‘%T’)) > ‘13:01:00’ %}

    Tomorrow:

    Average: {{state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,

    ‘tomorrow’)|average|round(1)}}

    Min: {{state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,

    ‘tomorrow’)|min|round(1)}}

    Max: {{state_attr(‘sensor.nordpool_kwh_nl_eur_3_10_021’,

    ‘tomorrow’)|max|round(1)}}

    {%- else %}

    Electricity prices for tomorrow are not available until 13:00.

    {% endif -%}

You have to make one input number helper where you can choose how many hours you need. helper

The preferred way to configure an input number is via the user interface at Settings > Devices & Services > Helpers . Click the add button and then choose the Number option.

OK i am sorry i am kinda new to this and still learning
this is what i got do you have an example what to put there


1 Like

Thank you very much

1 Like

Hi arva, any luck in getting this to work? Looking for exactly the same thing with one addition. I also want a ticker showing total earning (self used Solar + sold Solar) deducted from the total investment of the solar plant.

Yes i did.

Screenshot 2023-11-29 at 12.06.40

Probably took too complex route, but got the result. As i have 3phase system and in Estonia our grid will count every phase separately, meaning that if i export 1kw and 1kw on 2 phases and import 2kw on 1 phase, I still have to pay the tariff on the 2kw. It’s quite unique approach in our country. Usually grid will sum phases.

What you need:

  1. Inverter production power (in my case power for each phase). My huawei is not giving this information and i needed to calculate it from the phases currents:
  - platform: template
    sensors:
      inverter_phase_1_power:
        friendly_name: "Inverter Phase 1 Power"
        unit_of_measurement: "W"
        value_template: >-
          {{ (states('sensor.inverter_phase_a_current') | float * 
              states('sensor.inverter_phase_a_voltage') | float *
              states('sensor.inverter_power_factor') | float) }}

  - platform: template
    sensors:
      inverter_phase_2_power:
        friendly_name: "Inverter Phase 2 Power"
        unit_of_measurement: "W"
        value_template: >-
          {{ (states('sensor.inverter_phase_b_current') | float * 
              states('sensor.inverter_phase_b_voltage') | float *
              states('sensor.inverter_power_factor') | float) }}

  - platform: template
    sensors:
      inverter_phase_3_power:
        friendly_name: "Inverter Phase 3 Power"
        unit_of_measurement: "W"
        value_template: >-
          {{ (states('sensor.inverter_phase_c_current') | float * 
              states('sensor.inverter_phase_c_voltage') | float *
              states('sensor.inverter_power_factor') | float) }}
  1. Power back to grid. I have Shelly EM3 and it gives a power entities for each phase. Negative result means power is going back to grid. I made template sensor that will show value when it’s negative and show it as as a positive number (Note: absolute value would be better than -1)
  - platform: template
    sensors:
      phase_1_power_back_to_grid:
        friendly_name: "Phase 1 Power Back to Grid"
        unit_of_measurement: "W"
        value_template: >-
          {% set power = states('sensor.phase_1_power') | float %}
          {{ [0, power * -1] | max }}

      phase_2_power_back_to_grid:
        friendly_name: "Phase 2 Power Back to Grid"
        unit_of_measurement: "W"
        value_template: >-
          {% set power = states('sensor.phase_2_power') | float %}
          {{ [0, power * -1] | max }}

      phase_3_power_back_to_grid:
        friendly_name: "Phase 3 Power Back to Grid"
        unit_of_measurement: "W"
        value_template: >-
          {% set power = states('sensor.phase_3_power') | float %}
          {{ [0, power * -1] | max }}
  1. Total back to grid power
  - platform: template
    sensors:
       total_power_back_to_grid:
        friendly_name: "Total Power Back to Grid"
        unit_of_measurement: "W"
        value_template: >
          {{ states('sensor.phase_1_power_back_to_grid') | float
          + states('sensor.phase_2_power_back_to_grid') | float
          + states('sensor.phase_3_power_back_to_grid') | float }}
  1. Total own used PV power
  - platform: template
    sensors:
      pv_own_usage_total_power:
        friendly_name: "Total PV Own Usage Power"
        unit_of_measurement: "W"
        value_template: >
          {% set phase_1_power = states('sensor.inverter_phase_1_power') | float %}
          {% set phase_1_back_to_grid = states('sensor.phase_1_power_back_to_grid') | float %}
          {% set phase_2_power = states('sensor.inverter_phase_2_power') | float %}
          {% set phase_2_back_to_grid = states('sensor.phase_2_power_back_to_grid') | float %}
          {% set phase_3_power = states('sensor.inverter_phase_3_power') | float %}
          {% set phase_3_back_to_grid = states('sensor.phase_3_power_back_to_grid') | float %}          
          {{ (phase_1_power - phase_1_back_to_grid) + (phase_2_power - phase_2_back_to_grid) + (phase_3_power - phase_3_back_to_grid)| round(2) }}

  1. Current benefit calculations. Needed for the Utility meter step to start counting. Back to grid I use Nordpool price without VAT. Own usage I use nordpool with VAT + networks tariffs.
  - platform: template
    sensors:
      pv_to_grid_benefit_now:
        friendly_name: 'PV to grid now'
        unit_of_measurement: "EUR"
        value_template: "{{ states('sensor.nordpool_kwh_ee_eur_3_10_0') | float * states('sensor.total_power_back_to_grid') | float / 1000 }}"

  - platform: template
    sensors:
      pv_own_usage_benefit_now:
        friendly_name: 'PV Own Usage Now'
        unit_of_measurement: "EUR"
        value_template: "{{ states('sensor.total_el_price') | float * states('sensor.pv_own_usage_total_power') | float / 1000 }}"
  1. Make Reiman Sum helpers to calculate cumulative benefit from the current benefit entities from last step. Settings > Devices & Services > Helpers > Create Helper > Integration - Reiman sum integral sensor
1.
Name: PV Own Usage Benefit Cumulative
Input Sensor: sensor.pv_own_usage_benefit_now
Integration method: Left
Precicion: 2
Time Unit: hours

2.  
Name: PV to grid cumulative Benefit
Input Sensor: sensor.pv_to_grid_benefit_now
Integration method: Left
Precicion: 2
Time Unit: hours
  1. (optional) Template sensor that will sum the cumulative own use and back to gid together
  - platform: template
    sensors:
      total_pv_all_time_benefit:
        friendly_name: "Total PV All Time Benefit"
        unit_of_measurement: '€' 
        value_template: >
          {% set pv_own_usage = states('sensor.pv_own_usage_benefit_cumulative') %}
          {% set pv_to_grid = states('sensor.pv_to_grid_cumulative_benefit') %}
          {% set pv_own_value = 0 if pv_own_usage == "unknown" else pv_own_usage | float %}
          {% set pv_to_grid_value = 0 if pv_to_grid == "unknown" else pv_to_grid | float %}
          {{ (pv_own_value + pv_to_grid_value) | round(2) }}
  1. So now we have entities that will cumulative current own usage and back to grid benefits. Next step is to make utility meters. You can do it in UI, but i preferres configuration.yaml, as there are quite a few of them (daily, monthly, yearly or all time if you want). Daily and Monthly are needed in my setup to show the apex chart corectly.
utility_meter:
  pv_own_usage_benefit_daily:
    source: sensor.pv_own_usage_benefit_cumulative
    name: PV Own Usage Benefit Daily
    cycle: daily
  pv_to_grid_benefit_daily:
    source: sensor.pv_to_grid_cumulative_benefit
    name: PV to Grid Benefit Daily
    cycle: daily

  pv_own_usage_benefit_monthly:
    source: sensor.pv_own_usage_benefit_cumulative
    name: PV Own Usage Benefit Monthly
    cycle: monthly
  pv_to_grid_benefit_monthly:
    source: sensor.pv_to_grid_cumulative_benefit
    name: PV to Grid Benefit Monthly
    cycle: monthly

  pv_own_usage_benefit_yearly:
    source: sensor.pv_own_usage_benefit_cumulative
    name: PV Own Usage Benefit Yearly
    cycle: yearly
  pv_to_grid_benefit_yearly:
    source: sensor.pv_to_grid_cumulative_benefit
    name: PV to Grid Benefit Yearly
    cycle: yearly
  1. Template to calculate the totals of daily, monthly and yearly benefits
  - platform: template
    sensors:
      total_pv_daily_benefit:
        friendly_name: "Total PV Daily Benefit"
        unit_of_measurement: '€' 
        value_template: >
          {% set pv_own_usage = states('sensor.pv_own_usage_benefit_daily') %}
          {% set pv_to_grid = states('sensor.pv_to_grid_benefit_daily') %}
          {% set pv_own_value = 0 if pv_own_usage == "unknown" else pv_own_usage | float %}
          {% set pv_to_grid_value = 0 if pv_to_grid == "unknown" else pv_to_grid | float %}
          {{ (pv_own_value + pv_to_grid_value) | round(2) }}

  - platform: template
    sensors:
      total_pv_monthly_benefit:
        friendly_name: "Total PV Monthly Benefit"
        unit_of_measurement: '€' 
        value_template: >
          {% set pv_own_usage = states('sensor.pv_own_usage_benefit_monthly') %}
          {% set pv_to_grid = states('sensor.pv_to_grid_benefit_monthly') %}
          {% set pv_own_value = 0 if pv_own_usage == "unknown" else pv_own_usage | float %}
          {% set pv_to_grid_value = 0 if pv_to_grid == "unknown" else pv_to_grid | float %}
          {{ (pv_own_value + pv_to_grid_value) | round(2) }}

  - platform: template
    sensors:
      total_pv_yearly_benefit:
        friendly_name: "Total PV Yearly Benefit"
        unit_of_measurement: '€' 
        value_template: >
          {% set pv_own_usage = states('sensor.pv_own_usage_benefit_yearly') %}
          {% set pv_to_grid = states('sensor.pv_to_grid_benefit_yearly') %}
          {% set pv_own_value = 0 if pv_own_usage == "unknown" else pv_own_usage | float %}
          {% set pv_to_grid_value = 0 if pv_to_grid == "unknown" else pv_to_grid | float %}
          {{ (pv_own_value + pv_to_grid_value) | round(2) }}

  1. Apex chart code:
type: custom:apexcharts-card
graph_span: 31d
span:
  start: month
stacked: true
header:
  show: true
  title: PV Savings Current Month
  show_states: true
  colorize_states: true
series:
  - entity: sensor.pv_own_usage_benefit_daily
    name: Own usage
    yaxis_id: first
    type: column
    group_by:
      func: max
      duration: 1d
    show:
      in_header: false
      legend_value: false
  - entity: sensor.pv_to_grid_benefit_daily
    name: To Grid
    yaxis_id: first
    type: column
    group_by:
      func: max
      duration: 1d
    show:
      in_header: false
      legend_value: false
  - entity: sensor.total_pv_daily_benefit
    name: Total
    yaxis_id: first
    type: line
    stroke_width: 0
    unit: €
    show:
      extremas: true
      in_header: false
      legend_value: false
    group_by:
      func: max
      duration: 1d
  - entity: sensor.total_pv_monthly_benefit
    unit: €
    yaxis_id: second
    name: Total
    show:
      legend_value: false
      in_chart: false
  - entity: sensor.pv_own_usage_benefit_monthly
    unit: €
    yaxis_id: second
    name: Own Usage
    show:
      legend_value: false
      in_chart: false
  - entity: sensor.pv_to_grid_benefit_monthly
    unit: €
    yaxis_id: second
    name: PV to Grid
    show:
      legend_value: false
      in_chart: false
yaxis:
  - id: first
    decimals: 2
    apex_config:
      tickAmount: 2
      logarithmic: false
  - id: second
    show: false

And the result should be like this (Its’ winter in Estonia, so no sun):
Screenshot 2023-11-29 at 12.06.40

And to have investment VS benefit you should to utility meter that will not reset and then compare this number to your investment. Make some template sensor that will just subtract from the investment or does a presentage calculation or if you have bought the PV system with financing compare monthly and yearly cost vs benefit or whatever you can think of.

6 Likes

@arva you’re a ROCK STAR! Thanks for all the effort of writing everything down :100:

1 Like

Old topic, but I just needed the nordpool array prices with tariff. Template to add to the tariff field in UI when aading new nordpool device:

{% set d = {
          "operator_margin": 0.000, 
          "electricity_excise": 0.001,
          "renewable_tariff": 0.0113,
          "nw_fee_night": 0.021,
          "nw_fee_day": 0.0369
      } %}
      {% if now().weekday() in (5, 6) or now().hour < 7 or now().hour >= 22 %}
        {{ ((d.operator_margin + d.renewable_tariff + d.electricity_excise + d.nw_fee_night) * 1.2) | float | round(4) }}
      {% else %}
        {{ ((d.operator_margin + d.renewable_tariff + d.electricity_excise + d.nw_fee_day) * 1.2) | float | round(4) }}
      {% endif %}

@arva In your code / utility_meter you’re referencing to sensor.pv_own_usage_benefit_cumulative and sensor.pv_to_grid_cumulative_benefit, to a noob can you pls explain?

Is there a step between benefit.now and benefit.cumulative?