Tibber sensor for future price (tomorrow)

So, I’ve finally started working on this, but I’ve started a new topic to avoid confusion:

Tibber future (tomorrow) prices statistics - Share your Projects! - Home Assistant Community (home-assistant.io)

Hello, nice work. Thanks for the code.
I tried to build it for my setting. How do you make that the blue line is not visible on the left side of “now”?
My goal is to have the past line ending at “now” and the forecast line start at “now” . Do you know if this is possible? And how?
Thanks in advance.

Just tried your rest sensor out but it conflicts with the normal Tibber integration at start up and results in my HA takes 10min or so to fully load but the Tibber integration fails to initialize which leads to the Energy dashboard not working etc.

Do you have any idea what might be causing it?

Hi, they really don’t have anything in common, so the only thing I would suggest is to reduce refresh rate and see if next day it improves:

In the rest sensor change this:
scan_interval: 60

to maybe
scan_interval: 3600

which will make 1 query every 1 hour.

1 Like

Just tried that and it worked, one reboot but that’s 100% better than before :slight_smile:

I’ve stopped using the “normal Tibber integration”, not because I have a conflict, but because that is not needed after a couple of modifications on @p1ranha’s REST sensor in order to add the current price on its state (instead of an “OK”):

sensor:
    # https://community.home-assistant.io/t/tibber-sensor-for-future-price-tomorrow/253818/23
    - platform: rest
      name: Tibber Electricity Price
      resource: https://api.tibber.com/v1-beta/gql
      method: POST
      scan_interval: 900
      payload: '{ "query": "{ viewer { homes { currentSubscription { priceInfo { current { total currency level } today { total startsAt level } tomorrow { total startsAt level }}}}}}" }'
      json_attributes_path: "$.data.viewer.homes[0].currentSubscription.priceInfo"
      json_attributes:
        - current
        - today
        - tomorrow
      value_template: '{{ value_json["data"]["viewer"]["homes"][0]["currentSubscription"]["priceInfo"]["current"]["total"] }}'
      headers:
        Authorization: !secret tibber_api_token
        Content-Type: application/json
        User-Agent: REST

This doesn’t have the consumption statistics as the “official” integration and also doesn’t have the price level (for witch I have a template sensor to calculate, but also would be easy to add in this sensor if you don’t wanna have another sensor).

PS: Note I’m using 900 sec (15min) as scan interval, which is working fine for me and prevents taking too long to fetching for the new price when a new hour starts (this could be easily solved by a template sensor).

This is the other template sensor I’ve mentioned. It probably needs a review/clean up as I’m sure I’m not using half of those attributes available.
The result of this sensor is what I use for my energy dashboard, as if updates the current rate faster than the REST sensor (thanks to the longer scan interval I’ve used there):

template:
  - sensor:
    - name: Electricity price
      unique_id: d4c04abf-c258-4f0f-a61d-cc0170225081
      unit_of_measurement: "SEK/kWh"
      icon: mdi:currency-usd
      state: >-
        {% if this.attributes.future_prices | default('unknown') in ['unknown', 'unavailable'] or (this.attributes.future_prices | default([]) | map(attribute='total') | list | count) < 1 %}
          unknown
        {% else %}
          {{ this.attributes.future_prices |  default([]) | map(attribute='total') | list | first | float(0) }}
        {% endif %}
      attributes:
        current: >-
          {% if state_attr('sensor.tibber_electricity_price', 'current') %}
            {{ state_attr('sensor.tibber_electricity_price', 'current') }}
          {% else %}
            [ ]
          {% endif %}
        today: >-
          {% if state_attr('sensor.tibber_electricity_price', 'today') %}
            {{ state_attr('sensor.tibber_electricity_price', 'today') }}
          {% else %}
            [ ]
          {% endif %}
        tomorrow: >-
          {% if state_attr('sensor.tibber_electricity_price', 'tomorrow') %}
            {{ state_attr('sensor.tibber_electricity_price', 'tomorrow') }}
          {% else %}
            [ ]
          {% endif %}
        all_prices: "{{ this.attributes.today | default([]) + this.attributes.tomorrow | default([]) }}"
        #today_count: "{{ this.attributes.today | default([]) | map(attribute='total') | list | count }}"
        #tomorrow_count: "{{ this.attributes.tomorrow | default([]) | map(attribute='total') | list | count }}"
        #all_prices_count: "{{ this.attributes.all_prices | default([]) | map(attribute='total') | list | count }}"
        min_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | min | float(0) | round(4) }}
          {% endif %}
        max_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | max | float(0) | round(4) }}
          {% endif %}
        avg_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% elif (this.attributes.today | default([]) | map(attribute='total') | list | count) < 2 %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | max | float(0) | round(4) }}
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | average(0) | float(0) | round(4) }}
          {% endif %}
        price_level: >-
          {% if (this.attributes.current | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ this.attributes.current.level | default('unknown') | replace('_', ' ') | capitalize }}
          {% endif %}
        price_level_1d: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.avg_price | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        price_level_combined: >-
          {% set level1 = this.attributes.price_level_1d | default('unknown') %}
          {% set level3 = this.attributes.price_level | default('unknown') %}
          {% if level1 == level3 %}
            {{ level1 }}
          {% elif level1 in ['unknown','unavailable','none'] or level3 in ['unknown','unavailable','none'] %}
            unknown
          {% elif level1 == "Very cheap" %}
            {{ level3 }}
          {% elif level3 == "Very cheap" %}
            {{ level1 }}
          {% elif level1 == "Cheap" %}
            {{ level3 }}
          {% elif level3 == "Cheap" %}
            {{ level1 }}
          {% elif level1 == "Normal" %}
            {{ level3 }}
          {% elif level3 == "Normal" %}
            {{ level1 }}
          {% elif level1 == "Expensive" %}
            {{ level3 }}
          {% else %}
            {{ level1 }}
          {% endif %}
        is_below_average: >-
          {% if is_number(this.state) and is_number(this.attributes.avg_price) %}
            {{ (this.state | float(0) < this.attributes.avg_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_above_average: >-
          {% if is_number(this.state) and is_number(this.attributes.avg_price) %}
            {{ (this.state | float(0) > this.attributes.avg_price | float(0)) | lower}}
          {% else %}
            unknown
          {% endif %}
        is_at_min: >-
          {% if is_number(this.state) and is_number(this.attributes.min_price) %}
            {{ (this.state | float(0) <= this.attributes.min_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_at_max: >-
          {% if is_number(this.state) and is_number(this.attributes.max_price) %}
            {{ (this.state | float(0) >= this.attributes.max_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_close_to_min: >-
          {% if is_number(this.state) and is_number(this.attributes.min_price) %}
            {{ (this.state | float(0) <= (1.15 * this.attributes.min_price | float(0))) | lower }}
          {% else %}
            unknown
          {% endif %}
        future_prices: >-
          {% if (this.attributes.all_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.all_prices | default([])) | selectattr('startsAt', 'gt', (now() - timedelta(hours=1)) | string | replace(' ','T')) | list }}
          {% endif %}
        future_prices_totals: >-
          {% if (this.attributes.future_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices | default([])) | map(attribute='total') | list }}
          {% endif %}
        #future_prices_count: "{{ (this.attributes.future_prices_totals | default([])) | count }}"
        future_prices_min: |-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{  this.attributes.future_prices_totals | default([0]) | min | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_max: |-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_totals | default([0])) | max | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_avg: >-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_totals | default([0])) | average(0) | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_curr_price_level: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.future_prices_avg | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        future_prices_16h: >-
          {% if (this.attributes.future_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices | default([]) | list)[0:16]}}
          {% endif %}
        future_prices_16h_totals: >-
          {% if (this.attributes.future_prices_16h | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices_16h | default([])) | map(attribute='total') | list }}
          {% endif %}
        #future_prices_16h_count: "{{ (this.attributes.future_prices_16h_totals | default([])) | count }}"
        future_prices_16h_min: |-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | min | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_max: |-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | max | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_avg: >-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | average(0) | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_current_price_level: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.future_prices_16h_avg | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        future_prices_16h_current_price_is_close_to_min: >-
          {% if is_number(this.state) and is_number(this.attributes.future_prices_16h_min) %}
            {{ (this.state | float(0) <= (1.15 * this.attributes.future_prices_16h_min | default(0) | float(0))) | lower }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_current_price_is_at_min: >-
          {% if this.state | default(0) | float(0) > 0 and this.attributes.future_prices_16h_min | default(0) | float(0) > 0 %}
            {{ (this.state | default(0) | float(0) <= this.attributes.future_prices_16h_min | default(0) | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
3 Likes

What is the sensor : sensor.electricity_price_xxxxxxxx ??

What about a solution such as this custom integration for Frank Energie is doing? They added future prices as an attribute on the sensor with the current price:

state_class: measurement
prices: 
- from: '2022-12-03T00:00:00+01:00'
  till: '2022-12-03T01:00:00+01:00'
  price: 0.4884
- from: '2022-12-03T01:00:00+01:00'
  till: '2022-12-03T02:00:00+01:00'
  price: 0.4481
[...snip...]
- from: '2022-12-03T23:00:00+01:00'
  till: '2022-12-04T00:00:00+01:00'
  price: 0.423

unit_of_measurement: €/kWh
attribution: Data provided by Frank Energie
device_class: monetary
icon: mdi:currency-eur
friendly_name: Current electricity price (All-in)

I really like this approach as it allows me to easily use these values in an automation, e.g. to schedule my dishwasher:

alias: Dishwasher - Auto schedule Eco50
description:
  Automatically schedules the Eco50 program during cheap electricity
  prices when the dishwasher door is closed and remote start is enabled.
trigger:
  - type: not_opened
    platform: device
    device_id: "{{ dishwasher_id }}"
    entity_id: binary_sensor.dishwasher_door
    domain: binary_sensor
condition:
  - condition: state
    entity_id: sensor.dishwasher_operation_state
    state: Ready
  - type: is_on
    condition: device
    device_id: "{{ dishwasher_id }}"
    entity_id: binary_sensor.dishwasher_remote_control
    domain: binary_sensor
action:
  - service: home_connect.start_program
    data:
      device_id: "{{ dishwasher_id }}"
      program: Dishcare.Dishwasher.Program.Eco50
      key: BSH.Common.Option.StartInRelative
      value: |
        {% set lowest_price = state_attr('sensor.current_electricity_price_all_in', 'prices') | selectattr('from', 'gt', now()) | selectattr('till', 'lt', now() + timedelta(hours=check_prices_hours_ahead)) | min(attribute='price') %}
        {% set delay = (lowest_price['from'] - now()).total_seconds() | int - delay_offset_seconds %}
        {{ delay if delay > 0 else 0 }}
      unit: seconds
variables:
  dishwasher_id: 5ea51af1fee5c7e8f4c1b7a8ee70b048
  delay_offset_seconds: 900  # takes over 15 minutes to use full power
  check_prices_hours_ahead: 9
mode: single

From what I can tell (though I may be wrong!), this is not possible with the current custom component as it only provides a graph.

3 Likes

I just signed up for Tibber and am getting ready to make the most of it. I had the other custom component running (that showed OK in it’s standard state) and am now trying to get yours working, but instead of showing OK it actually comes up empty.
Not sure what-äs going on…I don’t get any errors, but it doesn’t appear to be getting any data from the Tibber API despite the API key…

I’ve been in so many discussions around this Tibber data and have worked so much in my sensors that I don’t know which one you are testing right now, but will spend some time later this week to read back this thread and try to understand the issue you have,

Anyway, you can find in GitHub the Tibber library I have on production on my Home Assistant right now:

This custom component provides future prices: Add hourly price data by Knodd · Pull Request #34 · Danielhiversen/home_assistant_tibber_data · GitHub

It will be great to have this from the official integration, however I could see this was merged back in October and still not available in production.
Do you have any clue in how is this process?

Aha!

I just realized that is a different repository…

I will try out later.

By the way, I liked the experimental sensors:

Experimental and requires additional configuration:

  • Grid price (Only if your grid company is supported by Tibber)
  • Estimated total price with subsidy and grid price (Only if your grid company is supported by Tibber)
    How can I setup these additional configuration?

Thanks for your replies. Right now I think my problwm isn’t with the custom component at all, it’s simply that Tibber hasn’t “activated” my contract yet, so I don’t get any data from the API at all. I will keep waiting ans trying and see what happens once my switch to Tibber is complete. I might have to stick with Nordpool as a data source anyway because there are some nice integrations (like the EV Smart Charge) that rely und Nordpool data.
.

1 Like

Have you tried:

Optional for extra sensors:

tibber_data:
  email: Your email registred at Tibber
  password: Your Tibber password

… in conf.yaml

Yeah, I got that, however I’m still a little bit lost with all those new sensors…

I’ve some unknown and I couldn’t find the future prices.

@Danielhiversen, am I missing something?

Ok, yes sure looks like it’s the “unknown” who are interesting :slight_smile: , i don’t know if future_price is a separate, or whether it’s “included” in energy_price … which will get updated in about 1.5 hour
EDIT: Currently there is no “Tomorrows_Price” from Northpool
PS: Seems like there is no such thing as “future_price.sensor” , and as tomorrows_price is not “available” you get “unknown” — keep me posted :wink:

Ok, so now I have the Tibber sensors. It really was because my Tibber contract wasn’t confirmed yet. It’s still not active, but the date is set (February 10) so they apparently unlocked the API for my account.

Now I have Tibber, Nordpool and entso-e data…just need to figure out the best way to use it…:wink:

1 Like

I have a automation using HA and node-red with my bosh dishwasher that start it during night time based on the best price if the door is closed but have been opened since last run… Seem to be the same thing you try to do.

I still ironing out some quirks for the full integration but here is the tibber part, it has been stable (even if the js code is not as good as it could be).

What you get is a global variable called tibberBestPrices that contain objects called h1, h2 … h35. each of this object is an array of timeslots sorted by price.
So if you want echo50 that takes 5hours, you want to look at tibberBestPrices.h5[0].startsAt that contains the timestamp it’s cheapest to run for 5hours tibberBestPrices.h5[0].total contains the price per kwh.
I personaly check multiply the price Bosch SMU4HAW48S - Teleradio with the total of h5 (eco50) h2 (auto2) h1 (quick65). Most of the time echo50 is the most cheap option but it has happened once or twince that h1 or h2 have been cheaper, even if I’m not sure about that as I didn’t included the static cost per kwh back then.

Anyway this can be used to find the best price for anything that can be run automatically for a predefined time. (washing mashine with builtin dryer being what comes to mind first.)
Heating in basement/summer house or what ever could use this (in that case I would have a backup radiator targeting 10degrees and automisation targeting 15 degrees, just in case automisation fail.

I THINK you should be able to import this directly to node-red but you have to add tibber plugins by your self and change the sesors to the correct ones. (should be easy).

Hope it’s of some use. I’m a rookie in this community.

1 Like