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

sounds weird.
i did some small updates to the graph scripts today but mostly just some titles that where wrong. and a update to the template script some days ago where tomorrow gets data at minutes: 10 (i saw that pulling data to two templates at once from nordpool was giving some errors so did a little bit of delay for the tomorrows data and that seem to work)

the nordpool integration usually takes in new data within an hour after release. and tomorrows data template updates every hour. so might take up to two hours after nordpool releases new prices. you can change the update interval for tomorrows template.

also. the apex chart sometimes missbehaves and you need to reload the page to see tomorrows data. also try in another browser. if that doesn’t work. check logs if something misbehaves regarding the template. also check the state of tomorrows data under envelopment tools. to see if there is data from the template or if it gets stuck before that somehow

1 Like

also update your ha to latest core. some nordpool bugs are fixed in that

1 Like

Well, started over and redid the apex chart…
Now it seems to be working.

Is it possible to have the chart on auto scale?
~12 set and the price is low i tgives a lot of overhead and empty space…

And THANK YOU for the support and time you put into this!

regards
// Hans

Spoke to early… It work perfectly in web browser on pc.
And in HA app on android… BUT only in portrait view… Horizontal it’s just the date line and the header values that are visible.

Any ideas?

//Hans

its the reactive part that you might want to look into. its a bit buggy in apexchart and i’ve only adjusted it to my usercase. since it’s dependent on screen resolution you might want to do another graph for small devices

the reactive part? never heard about it.
Any pointers?

Edit: problem solved… didn’t really know how but changed PX values in the begining of the chart code… :slight_smile:

Again, thank you for the support and your work!

//Hans

Thank You guys! Now I get it :+1: The “key” was remove/uninstall the nordpool from HASC. Then install that Nordpool from the “devices”.

I have no Area. You have Sweden 1,2,3 and 4. I live in The Netherlands what do i need to put after area:

have you tried with NL as country and area?. since you already configure that in the nordpool integration. the data should be from the right place and in the template i just tried to mimic the hacs version data output. its not used by the graph so should be fine with whatever you put there.

Heya, I’m a bit lost. I just tried the new official Nordpool integration after using the HACS version for years. Now I’m left with seven sensors, but no daily prices or tomorrow’s prices. I understand that the new integration requires creating extra sensors via templates, but the documentation only shows an example for tomorrow’s prices. How do I create a sensor that shows today’s prices?
And I guess I now need to add VAT also via a template sensor?

@MrGlad8
have you read this thread with the examples? you get today and tomorrow. helpers for vat and such.

Should I just copy your code from the #5 post? Didnt get it tot work, will try again!

start from post 3. guide is sectioned.

the sensor doesn’t show date/time with the data


while for the next day

it does. is this the problem?

I have a strange behavior word the Core nordpool integration. At the start of the day (from 00:00 to 13:05), the template (code below) collects the data from yesterday as the today_price, and i don’t know why.
If i use a template action in the developer tools the collected data is fine, what am i missing?

- trigger:
    - trigger: time_pattern
      minutes: /15
    - trigger: homeassistant
      event: start
  action:
    - action: nordpool.get_prices_for_date
      data:
        config_entry: 01KB4QNAERVA78WH3Z93BYE47R
        date: "{{ now().date() + timedelta(days=1) }}"
        areas: BE
        currency: EUR
      response_variable: tomorrow_price
    - action: nordpool.get_prices_for_date
      data:
        config_entry: 01KB4QNAERVA78WH3Z93BYE47R
        date: "{{ now().date()}}"
        areas: BE
        currency: EUR
      response_variable: today_price
  sensor:
    - name: Tomorrow lowest price
      unique_id: nl_tomorrow_low_price
      unit_of_measurement: "EUR/kWh"
      icon: mdi:cash
      state: >
        {% if 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) %}
          {% for state in tomorrow_price['BE'] %}
            {% set data.prices = data.prices + [(state.price/1000) | round(3, default=0)] %}
          {% endfor %}
          {{min(data.prices)}}
        {% else %}
          unavailable
        {% endif %}
      attributes:
        data: >
          {% if tomorrow_price is mapping %}
          {% set electricity_tax = states('input_number.electricity_tax') | float(0) %}
          {% set purchase_costs = states('input_number.electricity_purchase_costs') | float(0) %}
          {% set data = namespace(prices=[]) %}
            {% for state in tomorrow_price['BE'] %}
              {% set data.prices = data.prices + [{'start':state.start, 'end':state.end, 'price': (state.price/1000) | round(3, default=0)}] %}
            {% endfor %}
            {{data.prices}}
          {% else %}
            []
          {% endif %}
    - name: Tomorrow highest price
      unique_id: nl_tomorrow_high_price
      unit_of_measurement: "EUR/kWh"
      icon: mdi:cash
      state: >
        {% if 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) %}
          {% for state in tomorrow_price['BE'] %}
            {% set data.prices = data.prices + [(state.price/1000) | round(3, default=0)] %}
          {% endfor %}
          {{max(data.prices)}}
        {% else %}
          unavailable
        {% endif %}
      attributes:
        data: >
          {% if tomorrow_price is mapping %}
          {% set electricity_tax = states('input_number.electricity_tax') | float(0) %}
          {% set purchase_costs = states('input_number.electricity_purchase_costs') | float(0) %}
          {% set data = namespace(prices=[]) %}
            {% for state in tomorrow_price['BE'] %}
              {% set data.prices = data.prices + [{'start':state.start, 'end':state.end, 'price': (state.price/1000) | round(3, default=0)}] %}
            {% endfor %}
            {{data.prices}}
          {% else %}
            []
          {% endif %}
    - name: Today prices
      unique_id: nl_today_prices
      unit_of_measurement: "EUR/kWh"
      icon: mdi:cash
      state: >
        {% if today_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) %}
          {% for state in today_price['BE'] %}
            {% set data.prices = data.prices + [(state.price/1000) | round(3, default=0)] %}
          {% endfor %}
          {{min(data.prices)}}
        {% else %}
          unavailable
        {% endif %}
      attributes:
        data: >
          {% if today_price is mapping %}
          {% set electricity_tax = states('input_number.electricity_tax') | float(0) %}
          {% set purchase_costs = states('input_number.electricity_purchase_costs') | float(0) %}
          {% set data = namespace(prices=[]) %}
            {% for state in today_price['BE'] %}
              {% set data.prices = data.prices + [{'start':state.start, 'end':state.end, 'price': (state.price/1000) | round(3, default=0)}] %}
            {% endfor %}
            {{data.prices}}
          {% else %}
            []
          {% endif %}

Change the code a bit, now its working:

- trigger:
  - trigger: time_pattern
    minutes: /15
  - trigger: homeassistant
    event: start
  action:
    - variables:
        area: NL
        currency: EUR
        config_entry: <your own config_ID>
        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(1) %}
        {{ 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) %}
            {% if today_price["NL"] | count > 0 %}
              {% for state in today_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) * (100 + vat) + (purchase_costs+ electricity_tax)*100) | round(1)}] %}
              {% endfor %}
            {% endif %}
            {% if tomorrow_price["NL"] | count > 0 %}
              {% 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) * (100 + vat) + (purchase_costs+ electricity_tax)*100) | round(1)}] %}
              {% endfor %}
            {% endif %}
            {{data.prices}}
          {% else %}
            []
          {% endif %}
type: custom:apexcharts-card
experimental:
  color_threshold: true
graph_span: 36h
header:
  title: Elektriciteitsprijs (cent/kWh)
  show: true
  show_states: true
  colorize_states: false
span:
  start: hour
  offset: "-8h"
yaxis:
  - id: €cent
    apex_config:
      tickAmount: 10
      forceNiceScale: true
now:
  show: true
  label: Nu
series:
  - entity: sensor.electricity_prices
    yaxis_id: €cent
    name: Bar
    type: column
    stroke_width: 0.1
    show:
      extremas: true
      in_header: true
      in_chart: true
      legend_value: false
    float_precision: 1
    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.average.map((record, index) => {
        return [record.start, record.value];
      });
  - entity: sensor.electricity_prices
    yaxis_id: €cent
    name: line
    curve: stepline
    stroke_width: 0.5
    show:
      extremas: false
      in_header: false
      legend_value: false
    data_generator: |
      return entity.attributes.average.map((record, index) => {
        return [record.start, record.value];
      });

looks allright with my code.

I have a question about AIO. I thought it would highlight the 8 cheapest hours by the config made by @xztraz But as you can see in the pictures there’s highlighted on some low hours, but not the lowest. Is this right, or have I accidentally changed something? Also thanks to @xztraz for making this super easy to set up.

it looks for the cheapest hours between
first_hour: 19
last_hour: 18

it will look better when next days data shows up.

Here is a bit of extended code for aio energy management with cheap and expensive hours. There are some input helpers to be able to fine tune values that need to be created for this to work.

i found the expensive setting to be usefull if you don’t want to turn something off if the price is low enought the whole day.

aio_energy_management:
  cheapest_hours:
    - nordpool_official_config_entry: 01K6AEXZV4HDRNYVJQEJB2R5GT
      unique_id: my_cheapest_hours
      name: My Cheapest Hours
      mtu: 15
      first_hour: 19
      last_hour: 18
      price_limit: input_number.electricity_cheap_price_calc
      starting_today: true
      number_of_hours: input_number.cheap_hours_setting
      sequential: false
      failsafe_starting_hour: 21
      calendar: false
    - nordpool_official_config_entry: 01K6AEXZV4HDRNYVJQEJB2R5GT
      unique_id: my_expensive_hours
      name: My Expensive Hours
      mtu: 15
      first_hour: 24
      last_hour: 23
      inversed: true
      price_limit: input_number.expensive_price_calc
      starting_today: true
      number_of_hours: input_number.expensive_hours_setting
      sequential: false
      calendar: false

And graphs for that (and alot of extra stuff)

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: binary_sensor.my_expensive_hours
    name: Expensive
    type: area
    curve: stepline
    yaxis_id: CHEAP
    opacity: 0.6
    stroke_width: 0
    color: "#800"
    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_expensive_hours
    yaxis_id: CHEAP
    name: Expensive 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: "#f00"
    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
  - entity: binary_sensor.my_expensive_hours
    yaxis_id: CHEAP
    name: Expensive Now
    transform: "return x === 'on' ? 1 : 0;"
    color: "#f00"
    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