Tibber - Schedul prices upcoming 24 hours prices!

Thank you very much, jonas21 - that helped me so much :grin: :+1:

Hi everyone,

I have now also tinkered with it a bit and am very happy with the result. Since the code in this thread helped me so much, I’ll post mine here too. Maybe it will help one or the other.

Many greetings
MrM

image

Apex Chart Config:

type: custom:apexcharts-card
experimental:
  color_threshold: true
apex_config: null
header:
  show: true
  title: Tibber Preisvorschau
  standard_format: true
  show_states: true
  colorize_states: false
now:
  show: true
hours_12: false
graph_span: 30h
span:
  start: day
yaxis:
  - id: kWh
    decimals: 3
    opposite: true
    max: 1000
    apex_config:
      tickAmount: 4
  - id: EUR
    decimals: 3
series:
  # Aktueller Verbrauch als Graph
  - entity: sensor.energieverbrauch_aktuell  # <--- You may edit this to your sensor
    type: area
    show:
      legend_value: false
      extremas: false
    name: Verbrauch
    stroke_width: 5
    curve: smooth
    color: '#ffbd5c'
    opacity: 0.5
    yaxis_id: kWh
    group_by:
      func: avg
      duration: 60min
  # Tibber - Verlauf
  - entity: sensor.tibber_preise  # <--- You may edit this to your sensor (if you use the configuration.yaml code from jonas21 it would be "tibber_prices" [generated from name])
    name: Tibber
    stroke_width: 5
    float_precision: 3
    curve: smooth
    color: '#ffbd5c'
    opacity: 1
    color_threshold:
      - value: 0.35
        color: '#a83232'
      - value: 0.27
        color: '#f0ec16'
      - value: 0.2
        color: '#00ff2f'
    show:
      legend_value: false
      extremas: false
      in_header: false
    extend_to: now
    yaxis_id: EUR
    data_generator: |
      var today = entity.attributes.today.map((record, index) => {
              return [record.startsAt, record.total];
            });
      var tomorrow = entity.attributes.tomorrow.map((record, index) => {
              return [record.startsAt, record.total];
            });

      return today.concat(tomorrow);
  # Tibber - Aktueller Preis
  - entity: sensor.electricity_price_tibber # <--- You may edit this to your sensor
    name: Aktueller Preis
    extend_to: now
    float_precision: 3
    show:
      extremas: false
      in_chart: false
    stroke_width: 5
    yaxis_id: EUR
2 Likes

im trying to use the rest sensor but i keep getting errors for each entry. i have several other rest sensors so i cant simply state this as a new one and none of the other lines work (json attribute, path ) etc.

has anyone modified this sensors to work with other sensors in config?

Never worked with “rest” but everything i try results in a failure. If i use the original template ill get:

"Error loading /config/configuration.yaml: mapping values are not allowed here
in "/config/configuration.yaml", line 68, column 9"
tibber_custom:
- platform: rest
    name: Tibber prices
    resource: https://api.tibber.com/v1-beta/gql
    method: POST
    scan_interval: 60
    payload: '{ "query": "{ viewer { homes { currentSubscription { priceInfo { today { total startsAt } tomorrow { total startsAt }}}}}}" }'
    json_attributes_path: "$.data.viewer.homes[0].currentSubscription.priceInfo"
    json_attributes:
      - today
      - tomorrow
    value_template: Ok
    headers:
      Authorization: "XYZ"
      Content-Type: application/json
      User-Agent: REST

Anyone could help please?

1 Like

This looks like it works quite well. I still have to see if it is more consistent than Nordpool and I have to get it to show me the full 48 hours (I already changed the config, but it didn’t seem to stick) but apart from that … great work!

Since this thread helped me a lot, I’ll also post my solution. It involves:

First I set up a rest sensor which gets the price data from tibber API:

- platform: rest
  name: Tibber Prices
  resource: https://api.tibber.com/v1-beta/gql
  method: POST
  payload: '{ "query": "{ viewer { homes { currentSubscription { status priceInfo { current { total } today { total } tomorrow { total } } } } } }" }'
  json_attributes_path: "$.data.viewer.homes[0].currentSubscription.priceInfo"
  json_attributes:
    - today
    - tomorrow
  value_template: "{{ value_json.data.viewer.homes[0].currentSubscription.priceInfo.current.total | float }}"
  scan_interval: 30
  headers:
    Authorization: !secret tibber_token
    Content-Type: application/json
    User-Agent: REST
  unit_of_measurement: EUR/kWh

This sensor will have 2 attributes “today” and “tomorrow” which will contain the prices. As a bonus, the sensor state will always be the current price.

With that you can create an apex chart like this:

type: custom:apexcharts-card
experimental:
  color_threshold: true
all_series_config:
  unit: Cent/kWh
apex_config:
  grid:
    show: true
    borderColor: '#E0E0E0'
  chart:
    height: 250px
  tooltip:
    enabled: true
    followCursor: false
    x:
      show: false
    fixed:
      enabled: true
header:
  show: true
  title: Strompreis
  show_states: true
  colorize_states: true
  standard_format: false
graph_span: 48h
now:
  show: true
  color: 9E9E9E
span:
  start: day
series:
  - entity: sensor.tibber_prices
    show:
      in_header: before_now
      name_in_header: false
    color_threshold:
      - value: 0
        color: 4DD0E1
      - value: 10
        color: 26A69A
      - value: 15
        color: 4CAF50
      - value: 20
        color: 7CB342
      - value: 25
        color: FBC02D
      - value: 30
        color: EF6C00
      - value: 40
        color: B71C1C
    type: line
    curve: stepline
    extend_to: false
    stroke_width: 4
    float_precision: 2
    data_generator: |
      const noon = new Date()
      noon.setHours(0, 0, 0, 0)
      const prices = entity.attributes.today.concat(entity.attributes.tomorrow);
      const data = [];
      for(let i = 0; i < prices.length; i++) {
        data.push([noon.getTime() + i * 1000 * 3600, prices[i].total * 100])
      }
      return data;

I’ll update this post as I improve this. Honestly, I do not know how this chart behaves tomorrow when future prices are not (yet) available. I hope it’s somehow robust to paste those two arrays and loop over the length.

Edit 1:

  • Removed the tibber_data HACS component because it fails to populate future prices.
  • Added a rest sensor that directly requests the tibber API (surprisingly simple).
18 Likes

good work my friend

Hi As a work around I implemented a simple Node-RED flow and use apex card to visualize.
NodeRED requests the data from tibber (you have to use your credentials) and updates a sensor with state and attributes.

The apex chard has the following code:

type: custom:apexcharts-card
header:
  show: true
  title: Tibber Price Today & Tomorrow(€-cent)
  show_states: false
graph_span: 36h
span:
  start: hour
yaxis:
  - decimals: 1
experimental:
  color_threshold: true
series:
  - entity: sensor.tibber_price_series
    transform: return x * 100;
    type: line
    curve: stepline
    extend_to: false
    stroke_width: 5
    data_generator: |
      return entity.attributes.timeseries.map((value, index) => {
        return [new Date(value).getTime(), 100 * entity.attributes.priceseries[index]];
      });
    color_threshold:
      - value: 30
        color: red
      - value: 25
        color: yellow
      - value: 20
        color: darkgreen

and the flow is here (scheduled every day at 13:15 as in Germany the update is on 13:00):

[{"id":"ac89eec644b66b2e","type":"ha-sensor","z":"7b21a369cedefa85","name":"Tibber Prices Time Series","entityConfig":"815231582d01f1bb","version":0,"state":"payload.status","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1090,"y":158,"wires":[[]]},{"id":"82dd34b7e63dd046","type":"json","z":"7b21a369cedefa85","name":"","property":"payload.attributes","action":"obj","pretty":false,"x":904,"y":158,"wires":[["ac89eec644b66b2e"]]},{"id":"ff5842d205ad0e39","type":"inject","z":"7b21a369cedefa85","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"15 13 * * *","once":true,"onceDelay":"300","topic":"","payload":"","payloadType":"date","x":104,"y":86,"wires":[["05e9b1460206b41b"]]},{"id":"55a48a0776be8d1f","type":"http request","z":"7b21a369cedefa85","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"https://api.tibber.com/v1-beta/gql","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"bearer","senderr":false,"headers":[{"keyType":"other","keyValue":"Content-Type","valueType":"other","valueValue":"application/json; charset=utf-8"}],"credentials":{},"x":444,"y":86,"wires":[["f318d09f3f936614"]]},{"id":"f318d09f3f936614","type":"change","z":"7b21a369cedefa85","name":"Select price arrays","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data.viewer.homes[0].currentSubscription.priceInfo","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":624,"y":86,"wires":[["f598e25f6df9363a","0c740a237787e3e9"]]},{"id":"f598e25f6df9363a","type":"change","z":"7b21a369cedefa85","name":"merge today, tomorrow","rules":[{"t":"set","p":"merged","pt":"msg","to":" $append($.payload.today, $.payload.tomorrow)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":852,"y":86,"wires":[["5a6f2365c28a9d85","2c9ec11c453528df"]]},{"id":"5a6f2365c28a9d85","type":"change","z":"7b21a369cedefa85","name":"reset payload status and attributes","rules":[{"t":"set","p":"attributes","pt":"msg","to":"{ \"timeseries\": $.merged.startsAt, \"priceseries\": $.merged.total } ","tot":"jsonata"},{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.status","pt":"msg","to":"$min($.merged.total)","tot":"jsonata"},{"t":"move","p":"attributes","pt":"msg","to":"payload.attributes","tot":"msg"},{"t":"set","p":"tibber_data","pt":"flow","to":"payload.attributes","tot":"msg","dc":true}],"action":"","property":"","from":"","to":"","reg":false,"x":674,"y":158,"wires":[["82dd34b7e63dd046","09a3af0f0dce6909"]]},{"id":"c320d7b8ecf23ea0","type":"comment","z":"7b21a369cedefa85","name":"Read Me: Get tibber prices and prepare to be send to sensor","info":"# Result\nSensor with: \n- status: minimum price today, tomorrow\nand 2 attributes\n- timeseries: array of time points as text\n- priceseries: array of prices as dobule\n\n# Customization\nin http request you have to enter your token\n\n# trigger\ninject at 13:15 every day as at that time the price\nfor next day are available.\n\n# error handling\nnone\n\n","x":230,"y":40,"wires":[]},{"id":"e1d268a9ce9e454a","type":"comment","z":"7b21a369cedefa85","name":"! Modify","info":"Use your token from tibber developer portal","x":424,"y":117,"wires":[],"icon":"font-awesome/fa-arrow-up"},{"id":"0c740a237787e3e9","type":"debug","z":"7b21a369cedefa85","name":"original","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":634,"y":40,"wires":[]},{"id":"2c9ec11c453528df","type":"debug","z":"7b21a369cedefa85","name":"merged","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":854,"y":40,"wires":[]},{"id":"09a3af0f0dce6909","type":"debug","z":"7b21a369cedefa85","name":"split x array / y array","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":691,"y":199,"wires":[]},{"id":"05e9b1460206b41b","type":"change","z":"7b21a369cedefa85","name":"set query","rules":[{"t":"set","p":"payload","pt":"msg","to":"{     \"query\": \"{ viewer { homes { currentSubscription{ priceInfo{ today { total startsAt } tomorrow { total startsAt } } } } } }\" }","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":291,"y":86,"wires":[["55a48a0776be8d1f"]]},{"id":"815231582d01f1bb","type":"ha-entity-config","server":"32e490af.a728b","deviceConfig":"12b46ce419b8d466","name":"tibber_price_series","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Tibber Price Series"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"entity_picture","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"32e490af.a728b","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30},{"id":"12b46ce419b8d466","type":"ha-device-config","name":"Tibber Prices","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""}]
1 Like

Thanks for the inspiration!
I have modified and simplified your solution a bit, mainly pertaining to the data_generator part.

With the REST sensor otherwise the same, modify the payload to the following:

payload: '{ "query": "{ viewer { homes { currentSubscription { priceInfo { current { total } today { total startsAt } tomorrow { total startsAt } } } } } }" }'

I have removed the status part as I didn’t care for that, and added startsAt to both today and tomorrow so that you get a UTC-offset date-time that we’ll use for the data_generator. An entry would look like this then:

- total: 0.2585
  startsAt: '2023-08-13T00:00:00.000+02:00'

The data_generator part can then be simplified to the following, removing the calculation based on the current date, and also using a slightly more descriptive map lambda instead of the for loop:

data_generator: >
  return [...entity.attributes.today,
  ...entity.attributes.tomorrow].map((price) => {
    return [new Date(price.startsAt), price.total * 100];
  });

This first creates a new array with the values of entity.attributes.today and entity.attributes.tomorrow. It then maps each entry of that new array to a new entry based on the startsAt attribute of the price entry, and the total multiplied by 100 to get Cents instead of Euros per kilowatt hour.

You could also use moment(price.startsAt) instead of new Date(price.startsAt) as Moment.js is automatically included by ApexCharts, but since “plain” JavaScript seems to yield the same result, I went for new Date(price.startsAt)

Honestly, I do not know how this chart behaves tomorrow when future prices are not (yet) available. I hope it’s somehow robust to paste those two arrays and loop over the length.

It is! I have used both your code and mine for a bit now and it works splendidly. If there’s no data for tomorrow, it will simply stop at the current day mark and add no info for the next day.

EDIT: Ah, now that I’ve tinkered with this myself I saw that further up, jonas21 already had a similar solution with only slightly different code. :sweat_smile:

1 Like

It’s going really well. Thank you very much for that. Question. I have 2 homes in Tibber. Is it possible to distinguish between them? Thanks Stefan

try and error. i have it. Just replace homes[0] with homes[1]. Thanks for the great work anyway!

Hi!

thanks for your example, this works nice.

is there any way i can add the power that has been used till now? with bars.

i have no clue how to implement this from other examples unfortunatly :relaxed:

Hey, I landed here while looking for a way to get the day-ahead prices into home-assistant.

Is there any chance we can get this into the official Tibber integration?

9 Likes

Do you want this for automation purposes or just a pretty graph?

Thank you very much for sharing your YAML-Code. Helped me out a lot.

While playing around with the API i found, that there is a “price_level” for the price with the following states:

NORMAL
The price is greater than 90 % and smaller than 115 % compared to average price.

CHEAP
The price is greater than 60 % and smaller or equal to 90 % compared to average price.

VERY_CHEAP
The price is smaller or equal to 60 % compared to average price.

EXPENSIVE
The price is greater or equal to 115 % and smaller than 140 % compared to average price.

VERY_EXPENSIVE
The price is greater or equal to 140 % compared to average price.

I reused your REST-sensor and modified it, so that i get the price level. YAML-code is like this:

# Tibber Preislevel
- platform: rest
  unique_id: tibber_price_level
  name: Tibber Price Level
  resource: https://api.tibber.com/v1-beta/gql
  method: POST
  payload: '{ "query": "{ viewer { homes { currentSubscription { status priceInfo { current { level } today { level } tomorrow { level } } } } } }" }'
  json_attributes_path: "$.data.viewer.homes[0].currentSubscription.priceInfo"
  json_attributes:
    - today
    - tomorrow
  value_template: "{{ value_json.data.viewer.homes[0].currentSubscription.priceInfo.current.level }}"
  scan_interval: 30
  headers:
    Authorization: !secret tibber_token
    Content-Type: application/json
    User-Agent: REST

The sensor gives the price_level as actual state and for today and tomorrow in the attributes.

At the moment i only use this for the following markdown-card:
image

type: markdown
content: |-
  {% if states("sensor.tibber_price_level") == "NORMAL" %}
    <ha-alert alert-type="info" title="Preislevel">  Das aktuelle Preislevel ist **normal**.</ha-alert>
  {% elif states("sensor.tibber_price_level") == "CHEAP" %}
    <ha-alert alert-type="success" title="Preislevel">  Das aktuelle Preislevel ist **günstig**.</ha-alert>
  {% elif states("sensor.tibber_price_level") == "VERY_CHEAP" %}
    <ha-alert alert-type="success" title="Preislevel">  Das aktuelle Preislevel ist **sehr günstig**.</ha-alert>
  {% elif states("sensor.tibber_price_level") == "EXPENSIVE" %}
    <ha-alert alert-type="warning" title="Preislevel">  Das aktuelle Preislevel ist **teuer**.</ha-alert>
  {% elif states("sensor.tibber_price_level") == "VERY_EXPENSIVE" %}
    <ha-alert alert-type="error" title="Preislevel">  Das aktuelle Preislevel ist **sehr teuer**.</ha-alert>
  {% endif %}

Has anyone here found a way to get the “minTotal” and “maxTotal”-values for “today” and “tomorrow” and mixed this with the “hourly”-list in the attributes of a home assistant entity?

I have no idea how to get “minTotal” and “maxTotal” into the JSON-String. I´m looking for a way to do it like this:

{
  viewer {
    homes {
      currentSubscription {
        priceInfo {
          today {
            total
            startsAt
            minTotal <- not included in "today"
            maxTotal <- not included in "today"
          }
          tomorrow {
            total
            startsAt
            minTotal <- not included in "tomorrow"
            maxTotal <- not included in "tomorrow"
          }
        }
      }
    }
  }
}

Hi there. Just wanted to say Thank you to you for this thread and your posts. It helped me out a lot to do a better start with tibber and home assistant.

I´ve created a custom-card to display my tibber prices, consumption and costs. It looks like this:


9 Likes

May you share your code? Looks very nice and would give me and maybe some other a good inspiration.
And it looks like you’re using an automation to charge your house battery from the grid. Maybe you can share this, too?
I just creates a dashboard where I have to setup charging the battery from the grid on each day (semi-manually).

Sure, no problem.

Card-Preview



The Card uses the following components:

YAML-Code for the Card and Sensors

For this you need:

  • Utility-Meters for Daily, Weekly, Monthly and Yearly Consumption and Costs (based on the Sensors from the Tibber-Integration)
  • A Template Sensor that shows the price-range from min to max on that day (see below)
  • A template Sensor that shows how close the current price is the max-today price in percent (see below)
  • 4 template sensors for the daily, weekly, monthly and yearly cost per kwh (see below)
  • a switch-entity from your home-battery to turn on/off charging (to charge energy when it is cheap)
  • a sensors that knows if it is before 13:00 (1pm) to show a message, that the prices for tomorrow will come after 1pm (see below)
  • input-boolean that shows if your home battery should charge or not
  • an automation to charge energy into your home-battery when price level is cheap or very cheap (see below)

Template-Sensors
You can setup the template sensors via UI as “helpers”. Here is the state-template for the sensors:

  • price-range
{{ ((state_attr("sensor.electricity_price_wester_esch_26b","max_price") | float(default=0) - state_attr("sensor.electricity_price_wester_esch_26b","min_price") | float(default=0)) * 100) | round(1)  }}
  • how close is the current price to the max price in percent
{{ (((states("sensor.electricity_price_wester_esch_26b") | float(default=0) - state_attr("sensor.electricity_price_wester_esch_26b","min_price") | float(default=0)) / (state_attr("sensor.electricity_price_wester_esch_26b","max_price") | float(default=0) - state_attr("sensor.electricity_price_wester_esch_26b","min_price") | float(default=0))) * 100) | round(0) }}

daily, weekly, monthly and yearly costs per kwh (replace “daily” with “weekly”, “monthly” or “yearly”)

{{ (states("sensor.tibber_kosten_daily") | float(default=0) / states("sensor.tibber_verbrauch_daily") | float(default=0) * 100) | float(default=0)  }}

Sensor that knows if it is before 13h (1pm)
image

Input-Boolean (Helper) that show if the home battery should be charged or not
image

Automation

The automation does the following things:

  • IF price-level changes to “cheap” or “very cheap”

  • AND IF home battery is below 95% charge AND the expected remaining solar energy today is lower then the capacity of the home battery

  • THEN start charging your home battery

  • ELSE IF price-level changes to “normal”, “expensive” or “very expensive”

  • OR home battery is above 95% charge

  • THEN stop charging your home battery

alias: Tibber - Speicher günstig laden
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.tibber_price_level
    to: VERY_CHEAP
    id: sehr günstig
  - platform: state
    entity_id:
      - sensor.tibber_price_level
    to: CHEAP
    id: günstig
  - platform: state
    entity_id:
      - sensor.tibber_price_level
    to: NORMAL
    id: normal
  - platform: state
    entity_id:
      - sensor.tibber_price_level
    to: EXPENSIVE
    id: teuer
  - platform: state
    entity_id:
      - sensor.tibber_price_level
    to: VERY_EXPENSIVE
    id: sehr teuer
  - platform: numeric_state
    entity_id:
      - sensor.senec_battery_charge_percent
    above: 95
    id: Speicher ist voll
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - Speicher ist voll
              - normal
              - teuer
              - sehr teuer
        sequence:
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.tibber_speicher_laden
            data: {}
          - type: turn_off
            device_id: ba6662be81284ab19a7ff2a25d6c1c64
            entity_id: 4938b65e9c7938a7bfebb628361a12fb
            domain: switch
            enabled: true
      - conditions:
          - condition: trigger
            id:
              - sehr günstig
              - günstig
          - condition: numeric_state
            entity_id: sensor.senec_battery_charge_percent
            below: 95
          - condition: numeric_state
            entity_id: sensor.solar_restproduktion_heute
            below: 5
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.tibber_speicher_laden
            data: {}
          - type: turn_on
            device_id: ba6662be81284ab19a7ff2a25d6c1c64
            entity_id: 4938b65e9c7938a7bfebb628361a12fb
            domain: switch
            enabled: true
mode: single

19 Likes

Love it. That’s not far what I’m planning to do.
Gives me a lot of ideas and inspiration. Thank you very much.

1 Like

Lot’s of interesting approaches here.

I have a related Tibber use case: I use Home Assistant to activate my thermostat/heat pump when energy rates are lowest.

Since it doesn’t make sense to cycle a heat pump on-and off every hour I made my solution a bit smarter: I search for three continuous blocks of 4 hours in length where the average price is lowest (so 12 hours per day in total when heating is available).

To achieve this I created an external Python script that runs once per day to obtain pricing data from Tibber, I then use the Home Assistant API to plan calendar events for the time windows that I want my heating to be available. Finally the calendar triggers an automation that activates or deactivates a generic thermostat that controls my heating.

The title of each event contains the average price for the four-hour span (including tax, rounded to two decimal places):

Hourly prices are added to the description of each span:

image

Using a calendar to show hourly pricing would be easy: simply plan events for every hour with the pricing info in the title.

2 Likes