Tibber Prices with APEX-Charts using Service tibber.get_prices

Hi Guys,

since I was searching quite some time and could not find a solution, how to pipe in the data from a service (in my case the tibber price “forecast”) into the apex charts card, here is my solution, that took me around three hours to digg through (it’s what moles do) the ApexChartsCard integration and trying almost thousands of variants of JavaScript code in the browser console:

type: custom:apexcharts-card
graph_span: 48h
update_interval: 15s
apex_config:
  chart:
    height: 500px
  legend:
    position: top
    horizontalAlign: right
    float_precision: 2
show:
  last_updated: true
span:
  start: day
header:
  show: true
  title: Strompreise
  show_states: false
  colorize_states: true
yaxis:
  - id: price
    min: ~10
    decimals: 0
  - id: soc
    opposite: true
    min: ~0
    max: 100
    decimals: 0
now:
  show: true
  color: red
  label: Jetzt
series:
  - entity: sensor.zuhause_strompreis
    yaxis_id: price
    show:
      extremas: true
      offset_in_name: false
      legend_value: false
    type: area
    curve: stepline
    stroke_width: 2
    unit: ct/kWh
    data_generator: |
      var result = await hass.connection.sendMessagePromise({
          type: 'call_service',
          domain: 'tibber',
          service: 'get_prices',
          return_response: true
      }).then((result) => {
          var data = Object.values(result.response.prices)[0];
          return data.map((p) => { return [p.start_time, p.price * 100]; });
      }).catch((error) => {
          console.error('Error calling service:', error);
      });
      return result;
  - entity: sensor.zuhause_strompreis
    name: Aktueller Preis
    yaxis_id: price
    show:
      extremas: true
      offset_in_name: false
      legend_value: true
    stroke_width: 5
    float_precision: 2
    type: line
    unit: ct/kWh
    extend_to: false
    group_by:
      fill: "null"
    data_generator: |
      var now = new Date();
      return [ 
       [ now.getTime(), entity.state * 100.0], 
       [] // This avoids a strange s-curve...
      ];
  - entity: input_number.batterie_ladepreis
    yaxis_id: price
    name: Ladepreis
    color: red
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false
      datalabels: true
      offset_in_name: false
    data_generator: |
      var startOfDay = new Date();
      startOfDay.setHours(0);
      startOfDay.setMinutes(0);
      startOfDay.setSeconds(0);
      return [[startOfDay.getTime(), entity.state]];
      return [[startOfDay.getTime(), entity.state]];
  - entity: sensor.victron_battery_soc
    name: Akku SoC
    yaxis_id: soc
    color: green
    type: line
    stroke_width: 2
    extend_to: now
view_layout:
  position: main

I also added the state of charge from the home battery storage and the current value of the charging threshold (if price drops below, battery will be charged).

Here is, how it looks like:

Sorry for the German labels, but this is the Smart Home of my father not speaking english at all :grimacing:

The snapshot is one of the rare days, the price jumps into sky and it was the real reason to implement this for my fathers convenience…

Hope it helps others to avoid using node-red (as I still do in my SmartHome) or searching for wired solutions :stuck_out_tongue:

Have

3 Likes

OK, here is an update to get the full two days of forecast (it was retrieving only the first day)…

type: custom:apexcharts-card
graph_span: 48h
update_interval: 5s
apex_config:
  chart:
    height: 500px
    width: 100%
  legend:
    position: top
    horizontalAlign: right
    float_precision: 2
show:
  last_updated: true
span:
  start: day
header:
  show: true
  title: Strompreise
  show_states: false
  colorize_states: true
yaxis:
  - id: price
    min: ~10
    decimals: 0
  - id: soc
    opposite: true
    min: ~0
    max: 100
    decimals: 0
now:
  show: true
  color: red
  label: Jetzt
series:
  - entity: sensor.zuhause_strompreis
    yaxis_id: price
    show:
      extremas: true
      offset_in_name: false
      legend_value: false
    type: area
    float_precision: 2
    curve: stepline
    stroke_width: 2
    unit: ct/kWh
    data_generator: |
      var xnow = moment().startOf("day").add("days", 2); 
      var strtwoday = xnow.format("YYYY-MM-DD HH:mm:ssZ");
      console.log(strtwoday);
      var result = await hass.connection.sendMessagePromise({
          type: 'call_service', 
          domain: 'tibber', 
          service: 'get_prices',
          return_response: true, 
          service_data: { end: strtwoday }
      }).then((result) => {
          var data = Object.values(result.response.prices)[0];
          return data.map((p) => { return [p.start_time, p.price * 100]; });
      }).catch((error) => {
          console.error('Error calling service:', error);
      });
      return result;
  - entity: sensor.zuhause_strompreis
    name: Aktueller Preis
    yaxis_id: price
    show:
      extremas: true
      offset_in_name: false
      legend_value: true
    stroke_width: 5
    float_precision: 2
    type: line
    unit: ct/kWh
    extend_to: false
    group_by:
      fill: "null"
    data_generator: |
      var now = new Date();
      return [ 
       [ now.getTime(), entity.state * 100.0], 
       [] // This avoids a strange s-curve...
      ];
  - entity: input_number.batterie_ladepreis
    yaxis_id: price
    name: Ladepreis
    color: red
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false
      datalabels: true
      offset_in_name: false
    data_generator: |
      var startOfDay = new Date();
      startOfDay.setHours(0);
      startOfDay.setMinutes(0);
      startOfDay.setSeconds(0);
      return [[startOfDay.getTime(), entity.state]];
      return [[startOfDay.getTime(), entity.state]];
  - entity: input_number.mindest_soc_entladen
    yaxis_id: soc
    name: Ladeschluss SOC
    color: blue
    opacity: 0.5
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false
      datalabels: true
      offset_in_name: false
    data_generator: |
      var startOfDay = new Date();
      startOfDay.setHours(0);
      startOfDay.setMinutes(0);
      startOfDay.setSeconds(0);
      return [[startOfDay.getTime(), entity.state]];
      return [[startOfDay.getTime(), entity.state]];
  - entity: input_number.mindest_soc_laden
    yaxis_id: soc
    name: Entladeschluss SOC
    color: green
    opacity: 0.5
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false
      datalabels: true
      offset_in_name: false
    data_generator: |
      var startOfDay = new Date();
      startOfDay.setHours(0);
      startOfDay.setMinutes(0);
      startOfDay.setSeconds(0);
      return [[startOfDay.getTime(), entity.state]];
      return [[startOfDay.getTime(), entity.state]];
  - entity: sensor.victron_battery_soc
    name: Akku SoC
    yaxis_id: soc
    color: green
    type: line
    stroke_width: 2
    extend_to: now
view_layout:
  position: main

2 Likes

Thanks for this! Could you tell me where you’re getting this sensor from:

This is just a placeholder for the tibber price sensor, that is named differently on every installation e.g., sensor.electricity_price_examplestreet_123. But this should be the similar for every dynamically priced electricity provider that has some API to get the prices from.
Since the Tibber integration sensor is in €/kWh, it is useful to convert it to ct/kWh with a template or a template helper sensor.

Hope that helps :slight_smile:

Helps a lot! thanks!

Perfect - thank you.
I finally have added the charging threshold for my EV! :slight_smile:

1 Like

this is great @the78mole - thanks a lot.
I am struggeling to increase the width of the chart.
If I set:
width: 150%
it get’s cut off and doesn’t increase the size of the whole chart.
any hint would be much appriciated.

I also had some issues with width and also with max-value-scaling… Unfortunately, I don’t know a solution to it beside trial and error…

Thanks a lot Daniel for sharing this card. That helped me displaying the prices in my frontend.
But I have a question: What are the input_number entities for and how did you made them?

Hi Benny,
that is really simple. Go to Home Assistants Settings → Devices → Helper and create a new Input Number (e.g. a slider). Hope you can map it to the English version of Home Assistant, because mine is “unfortunately” in German.


Regards,
Daniel

Hi Daniel.
I’m German too, so I really appreciate your screenshots, because I can see directly what it is and where it is. :slight_smile:

Thanks for the explanation. Am I understanding that correctly, that you set the entity: input_number.batterie_ladepreis as a static value to see, where it is good to load the battery by grid power?

Exactly, I define prices for charging and discharging. I only have a small battery and only a tiny solar (2 x 420W + 2 x 370 W) compared to the power the house needs. So I mostly use power from the grid, but I use the battery to take advantage on low (and high) prices. I also shut down my heat pump, when the price is very high, while I adjust the target-temperatures also to the electricity price. (15 ct / kWh → +5°C, 30 ct/kWh → -5 °C).

1 Like

This is great, thanks!!

In my previous Home Automation (OpenHAB) I was also able to highlight the actual prices in the next 24 hours during which I could best charge my car (or battery in your case) so I could easily determine that minimum charge price to fully charge the car…

So for instance:
My car charges approx 10% in 45 minutes. If my SoC is 50% and I want to charge it to 90%, it would take 3 hours to charge the car. The system would then highlight the 3 cheapest hours in the period after now (till midnight if the next day prices were not yet available or even till midnight next day if they were). I’d either set that minimum price, or start charging at the highlighted periods… Will still need to figure out how to do that with HASS…