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

2 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

1 Like

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