Nordpool (ha integration) with 15min data. apexcharts and more. Updated

i wasn’t sure the hacs version would do 15 minute data. so i used the core version instead that have 15 min data and it seem to work. with the template you get datatables and such for the graphs. only thing is, most nordpool automation blueprints seem to only work with hacks version and only support hourly data anyway. but wit aio automation works on 15 min data.

when tomorrow prices appear. there seem to be a bug where min max and mean cant be read in apexchart. probably because data is above 16k limit. not sure how to solve this. maybe split data in today and tomorrow data sets. i’ll have a think about it…

pasted latest script at top.

where do you change stepsize for cost to 0.0001?

when you create the helper. and as i wrote. to have that option you need to have “advanced mode” enabled in your user settings (lower left, push your picture)

Where should i place the template?

I am using the data_generator option in apexcharts to create the average and min/max values. That reduces the size of the entity’s data. Here is my version, inspired by yours. Also to help out my Dutch friends. :slight_smile:

This data is placed in a file called template.yaml and is imported in configuration.yaml. So you’ll miss the "template: "statement.

- trigger:
  - trigger: time_pattern
    minutes: /15
  - trigger: homeassistant
    event: start
  action:
    - variables:
        area: NL
        currency: EUR
        config_entry: <your_config_entry>
        price_now: sensor.nord_pool_nl_current_price
        price_low: sensor.nord_pool_nl_lowest_price
        price_high: sensor.nord_pool_nl_highest_price
    - action: nordpool.get_price_indices_for_date
      data:
        config_entry: "{{ config_entry }}"
        date: "{{ now().date() }}"
        areas: "{{ area }}"
        currency: "{{ currency }}"
        resolution: "15"
      response_variable: today_price
    - action: nordpool.get_price_indices_for_date
      data:
        config_entry: "{{ config_entry }}"
        date: "{{ now().date() + timedelta(days=1) }}"
        areas: "{{ area }}"
        currency: "{{ currency }}"
        resolution: "15"
      response_variable: tomorrow_price
  sensor:
    - name: Electricity prices
      unique_id: nl_electricity_prices
      unit_of_measurement: "cent/kWh"
      icon: mdi:cash
      state: > # returns the current price
        {% set electricity_tax = states('input_number.electricity_tax') | float(0) %}
        {% set purchase_costs = states('input_number.electricity_purchase_costs') | float(0) %}
        {% set vat = states('input_number.VAT') | float(0) %}
        {% set current_price = (states(price_now) | float(0) + electricity_tax + purchase_costs) * (100 + vat) | round(3, default=0) %}
        {{ current_price }}
      attributes:
        tomorrow_valid: >
          {{ tomorrow_price[NL] | count > 0 }}
        average: >
          {% if (today_price is mapping) and (tomorrow_price is mapping) %}
            {% set data = namespace(prices=[]) %}
            {% set electricity_tax = states('input_number.electricity_tax') | float(0) %}
            {% set purchase_costs = states('input_number.electricity_purchase_costs') | float(0) %}
            {% set vat = states('input_number.VAT') | float(0) %}
            {% for state in today_price['NL'] %}
              {% set data.prices = data.prices + [(state.price/1000 + electricity_tax + purchase_costs) * (100 + vat) | round(3)] %}
            {% endfor %}
            {% for state in tomorrow_price['NL'] %}
              {% set corrected_start = as_datetime(state.start).astimezone().isoformat() %}
              {% set corrected_end = as_datetime(state.end).astimezone().isoformat() %}
              {% set data.prices = data.prices + [{'start':corrected_start, 'end':corrected_end, 'value': (state.price/1000 + electricity_tax + purchase_costs) * (100 + vat) | round(3, default=0)}] %}
            {% endfor %}
            {{data.prices}}
          {% else %}
            []
          {% endif %}

And here’s the code for the apexchart. Note that I am using the “area” format. I am still working on showing only one day if data for tomorrow is not yet available.

I was not bothered about splitting min/max values over today or tomorrow, so these were omitted.

type: custom:apexcharts-card
experimental:
  color_threshold: true
graph_span: 2d
header:
  title: Elektriciteitsprijs (cent/kWh)
  show: true
  show_states: true
  colorize_states: false
span:
  end: day
  offset: +1d
yaxis:
  - id: €cent
    apex_config:
      tickAmount: 6
      forceNiceScale: true
now:
  show: true
  label: Nu
series:
  - entity: sensor.electricity_prices
    yaxis_id: €cent
    name: Huidig
    type: area
    stroke_width: 0
    show:
      extremas: true
      in_header: false
      in_chart: true
      legend_value: false
    float_precision: 2
    color_threshold:
      - value: 0
        color: "#8be9fd"
      - value: 10
        color: "#50fa7b"
      - value: 15
        color: "#8be978"
      - value: 20
        color: "#f8f872"
      - value: 25
        color: "#ffb86c"
      - value: 30
        color: "#ff9859"
      - value: 35
        color: "#ff7846"
      - value: 40
        color: "#ff5555"
      - value: 50
        color: "#ff0000"
    data_generator: |
      return entity.attributes.data.map((start, index) => {
        return [new Date(start["start"]).getTime() , entity.attributes.data[index]["value"]];
      });
  - entity: sensor.electricity_prices
    yaxis_id: €cent
    name: Huidig
    show:
      in_header: true
      in_chart: false
    float_precision: 2
    unit: cent/kWh
    data_generator: |
      return entity.attributes.data.map((start, index) => {
        return [new Date(start["start"]).getTime() , entity.attributes.current];
      });
  - entity: sensor.electricity_prices
    attribute: average
    color: green
    yaxis_id: €cent
    name: Gemiddeld
    show:
      legend_value: false
    type: line
    stroke_width: 2
    float_precision: 2
    data_generator: |
      return entity.attributes.data.map((start, index) => {
        return [new Date(start["start"]).getTime() , entity.attributes.average];
      });

Thank you very much!

min max and mean in the dataset might be used for automations and such. but if you just want to show them in the graph thats a way of doing it.

i tried to get some of the hacs versions data into the dataset for a bit more compatiblity :slight_smile:

Somehow my chart looks like this:

What could be the problem? Data_generator mayby? Data seems ok in sensor.electricity_prices -entity.

Code from @Stolsel.

1 Like

I am having the same issue with the graph not loading

i changed the time pattern for the second half of the template to
minutes: “10”
seem like nordpool didn’t like running them both at the same time.

corrected the script in the first posts.

Advanced graph

remove what you don’t need

there is a power sensor. and i use Dynamic Energy Cost from HACS to calculate current price with the power used now.

the graph also shows future low price planning from aio energy management.

type: custom:apexcharts-card
experimental:
  color_threshold: true
apex_config:
  responsive:
    - breakpoint: 500
      options:
        chart:
          height: 300px
    - breakpoint: 1200
      options:
        chart:
          height: 640px
    - breakpoint: 3000
      options:
        chart:
          height: 800px
  legend:
    show: false
  title:
    floating: false
    align: center
    style:
      fontSize: 20px
      fontWeight: bold
  xaxis:
    labels:
      datetimeFormatter:
        hour: HH
  plotOptions:
    bar:
      columnWidth: 80%
      barGap: 3
graph_span: 2d
show:
  last_updated: true
header:
  title: Energy Price
  show: true
  show_states: true
  colorize_states: true
span:
  start: day
now:
  show: true
  label: Now
series:
  - entity: binary_sensor.my_cheapest_hours
    name: Cheapest
    type: area
    curve: stepline
    yaxis_id: CHEAP
    opacity: 0.6
    stroke_width: 0
    color: "#008"
    show:
      extremas: false
      in_header: false
    data_generator: |
      let data = [];
      let periods = entity.attributes.list || [];
      periods.forEach((p) => {
        let s = new Date(p.start).getTime();
        let e = new Date(p.end).getTime();
        data.push([s, 1]);    // jump up at start
        data.push([e, 1]);    // stay high until end
        data.push([e, 0]);    // drop straight down
      });
      return data;
  - entity: binary_sensor.my_cheapest_hours
    yaxis_id: CHEAP
    name: Cheap Chart
    transform: "return x === 'on' ? 1 : 0;"
    curve: stepline
    type: area
    opacity: 0.2
    stroke_width: 2
    extend_to: false
    group_by:
      func: avg
      duration: 15m
    color: "#00f"
    show:
      extremas: false
      in_header: false
  - entity: sensor.electricity_prices_today
    yaxis_id: SEK
    type: column
    show:
      extremas: true
      in_header: false
    float_precision: 2
    color: green
    color_threshold:
      - value: 0
        color: "#00ffaa"
      - value: 0.25
        color: "#00ff55"
      - value: 0.5
        color: "#00ff00"
      - value: 0.75
        color: "#55ff00"
      - value: 1
        color: "#aaff00"
      - value: 1.5
        color: "#ffff00"
      - value: 2
        color: "#ffaa00"
      - value: 2.5
        color: "#ff5500"
      - value: 3
        color: "#ff0000"
      - value: 4
        color: "#ff0055"
      - value: 5
        color: "#ff00aa"
      - value: 6
        color: "#ff00ff"
      - value: 7
        color: "#ff34ff"
      - value: 9
        color: "#ff65ff"
      - value: 11
        color: "#ff98ff"
      - value: 13
        color: "#ffccff"
      - value: 15
        color: "#ffffff"
    data_generator: |
      return entity.attributes.data.map((start, index) => {
        return [new Date(start["start"]).getTime() + 450000, entity.attributes.data[index]["value"]];
      });
  - entity: sensor.electricity_prices_tomorrow
    yaxis_id: SEK
    type: column
    show:
      extremas: true
      in_header: false
    float_precision: 2
    color: green
    color_threshold:
      - value: 0
        color: "#00ffaa"
      - value: 0.25
        color: "#00ff55"
      - value: 0.5
        color: "#00ff00"
      - value: 0.75
        color: "#55ff00"
      - value: 1
        color: "#aaff00"
      - value: 1.5
        color: "#ffff00"
      - value: 2
        color: "#ffaa00"
      - value: 2.5
        color: "#ff5500"
      - value: 3
        color: "#ff0000"
      - value: 4
        color: "#ff0055"
      - value: 5
        color: "#ff00aa"
      - value: 6
        color: "#ff00ff"
      - value: 7
        color: "#ff34ff"
      - value: 9
        color: "#ff65ff"
      - value: 11
        color: "#ff98ff"
      - value: 13
        color: "#ffccff"
      - value: 15
        color: "#ffffff"
    data_generator: |
      return entity.attributes.data.map((start, index) => {
        return [new Date(start["start"]).getTime() + 450000, entity.attributes.data[index]["value"]];
      });      
  - entity: sensor.electricity_prices_today
    name: Current Price
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: orange
  - entity: sensor.total_power
    yaxis_id: kW
    name: Power Usage Chart
    color: white
    opacity: 0.8
    float_precision: 2
    show:
      extremas: false
      in_header: true
    type: line
    curve: stepline
    stroke_width: 2
    extend_to: false
    unit: kW
    transform: return x /1000;
    group_by:
      func: avg
      duration: 15m
  - entity: sensor.total_real_time_energy_cost
    name: Cost Now Chart
    yaxis_id: SEK
    float_precision: 2
    show:
      extremas: false
      in_header: true
    unit: Kr/h
    type: line
    curve: stepline
    stroke_width: 3
    color: purple
    opacity: 0.8
    extend_to: false
    group_by:
      func: avg
      duration: 15min
  - entity: sensor.electricity_prices_today
    attribute: min
    name: Min Today
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: green
  - entity: sensor.electricity_prices_today
    attribute: mean
    name: Average Today
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: yellow
  - entity: sensor.electricity_prices_today
    attribute: max
    name: Max Today
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: red
  - entity: sensor.electricity_prices_tomorrow
    attribute: min
    name: Min Tomorrow
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: darkgreen
  - entity: sensor.electricity_prices_tomorrow
    attribute: mean
    name: Average Tomorrow
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: brown
  - entity: sensor.electricity_prices_tomorrow
    attribute: max
    name: Max Tomorrow
    yaxis_id: SEK
    float_precision: 2
    show:
      in_header: true
      in_chart: false
    unit: Kr/kWh
    color: darkred
  - entity: binary_sensor.my_cheapest_hours
    yaxis_id: CHEAP
    name: Cheap Now
    transform: "return x === 'on' ? 1 : 0;"
    color: "#00f"
    show:
      in_header: true
      in_chart: false
yaxis:
  - id: SEK
    min: 0
    max: ~12
    apex_config:
      tickAmount: 6
      forceNiceScale: true
      title:
        text: SEK
  - id: kW
    min: 0
    max: ~6
    decimals: 0
    apex_config:
      tickAmount: 6
      forceNiceScale: true
      opposite: true
      title:
        text: kW
  - id: CHEAP
    min: 0
    max: 1
    show: false
    apex_config:
      tickAmount: 6
      forceNiceScale: true


1 Like

Could be anything. If you assume it’s the data_generator, you can store the values in an array and check it using your console.

Hi and thanks for the write up…
Coming from HACS version and have issues how to find my config_entry.

edit: found the config_entry… but now i dont have the sensors…

Sorry for the beginner questions…

regards
Hans

sensors should be named electricity_price_today and tomorrow.

you need to reload the config. and if you have problems. take a look in the logs to see whatsup

Hi, thanks for the answer…
Seems like i have both,


and i have values for today and tomorrow when i check.

Found this in the log:
homeassistant.exceptions.ServiceValidationError: There was a connection error connecting to the API. Try again later

regards
//Hans

check the states of the sensors. so they have 15 minute prices. (tomorrow won’t have values until after nordpool updates around mid day)

or have you got it to work?

assuming you have data. electricity price today you can use in your energy dashboard.

graphs can be made with examples above.

energy automation works well with aio energy management (links and example above)

to get dynamic energy cost. you need a power sensor reading your current powers usage. in this case Total Power (i use a shelly 3em)

get Dynamic Energy Cost integration via HACS

make a dynamic cost entity in Dynamic Energy cost wit Todays Price as electricity price sensor. and your Total Power (W) sensor as power sensor.

Name it " Total Dynamic Energy Cost" (that’s what is used in the advanced graph)

Hi and again, thanks for the help!
I have got it to work partly, i have the full graph today values.
But not the tomorrow values that are issued around 2pm here in sweden, only get 2 small bars at midnight. thats it.
No access to my home assistant from this pc. but i can post a pic tomorrow.
Appriciate all the help!

regards
Hans