Tibber - Schedul prices upcoming 24 hours prices!

Hi Ingo,

first thank you much sharing your awesome Tibber Card with Storage charging evaluation. Exactly what i need!

Could you please please update your first post from 01.2024 to the actual code changes? Also for me as a beginner it woud be great to get some more information at which path in homeassistant the Code fragments must be added.

Thanks a lot in advance!

1 Like

Hi,

I wanted to take up the two topics again because I can’t find any solutions online and my attempts are also coming to nothing…

  1. Always display two decimal places (example: 23.00)
    image

  2. Show the prices from the previous day and the future ones in a curve…
    image

I hope for your ideas / support :slight_smile:

Hi, I really appreciate the path you set here and good explanations. Can you help me out with the “binary_sensor.akku_aus_dem_netz_laden”? What the YAML for this one? I like that it shows the charging period and not just how the battery state increases.
Thanks

2 Likes

Hi. Sorry for the late answer. The YAML-Code for that one is:

{% set min_price = state_attr("sensor.electricity_price_wester_esch_26b","min_price") | float(default=0) %}
{% set max_price = state_attr("sensor.electricity_price_wester_esch_26b","max_price") | float(default=0) %}
{% set price = states("sensor.electricity_price_wester_esch_26b") | float(default=0) %}
{% set price_range = max_price - min_price %}
{% set price_above_min = price - min_price %}
{% set price_below_max = max_price - price %}
{% set price_level = (price_above_min / price_range) * 100 %}
{% set price_difference_to_max = (price_below_max / price_range) * 100 %}
{% set price_min_tomorrow = states("sensor.template_tibber_price_min_tomorrow") | float(default=0) + 0.05 %}

{% set house_power = states("sensor.senec_house_power") | int(default=0) %}
{% set solar_power = states("sensor.senec_solar_generated_power") | int(default=0) %}

{% set solar_energy_expected = states("sensor.solar_restproduktion_heute") | float(default=0) %}
{% set charge_from_grid_below_expected_energy = states("input_number.laden_bei_restproduktion_unter") | float(default=0) %}

{% set battery_state = states("sensor.senec_system_state") %}

{% set max_price = state_attr('sensor.electricity_price_wester_esch_26b','max_price') | float %}
{% set current_price = states('sensor.electricity_price_wester_esch_26b') | float %}
{% set price_difference = max_price - current_price %}
{% set storage_capacity = 7.5 %}  # Kapazität deines Speichers in kWh
{% set charging_efficiency = 0.90 %}  # Ladeeffizienz
{% set economic_threshold = 0.10 %}  # Dein wirtschaftlicher Schwellenwert in €/kWh

{% if solar_power < house_power and price_difference_to_max > 50 and solar_energy_expected < charge_from_grid_below_expected_energy and price <= (min_price + 0.02) and price < price_min_tomorrow and battery_state != "AKKU VOLL" and price_difference >= economic_threshold %}
  true
{% else %}
  false
{% endif %}

But i decided to start over new on my approach to charge from grid. Here is my new solution:

Needed Entities:

1.) a template-binary_sensor to display if there is more solar-power coming in then my house needs. It is TRUE if there is NOT enough solar-power to feed energy into my battery.

{% set house_power = states("sensor.senec_house_power") | int(default=0) %}
{% set solar_power = states("sensor.senec_solar_generated_power") | int(default=0) %}
{{solar_power < house_power}}

2.) a template-binary_sensor to display if my battery is full or not. It is TRUE if my battery is NOT fully charged. This is based on an battery-state-sensor from my home-battery-integration.

{% set battery_state = states("sensor.senec_system_state") %}
{{battery_state != "AKKU VOLL"}}

3.) a template-binary_sensor to display if there is not much solar-energy expected for the rest of the day. It is TRUE if there ist NOT much energy expected. It is based on an input_number and the sensor for the expected energy from the Forecast.Solar-Integration

{% set solar_energy_expected = states("sensor.solar_restproduktion_heute") | float(default=0) %}
{% set charge_from_grid_below_expected_energy = states("input_number.laden_bei_restproduktion_unter") | float(default=0) %}
{{solar_energy_expected < charge_from_grid_below_expected_energy}}

4.) a trigger-binary_sensor that catches the prices for a specific amount of hours and decides if and when there is a good time to charge from grid. The decission is based on:

  • the price-difference between the cheapest and the most expensive price in the time-span
  • if the price-difference is above a specific value (input_number → for me 0.1 = 10 Cents)
template:
  - trigger:
      - platform: time_pattern
        hours: "/1"
      - platform: homeassistant
        event: start
    action:
      - service: tibber.get_prices
        data:
          start: "{{ (now()).strftime('%Y-%m-%d %H:') + '00:00' }}"
          end: "{{ (now() + timedelta(hours=states('input_number.tibber_cheap_energy_prices_hours') | int(default=24))).strftime('%Y-%m-%d %H:%M:%S') }}"
        response_variable: action_response
    binary_sensor:
      - unique_id: tibber_next_good_grid_charging_time
        name: tibber_next_good_grid_charging_time
# replace "Zuhause" with the name of your "home" in the tibber-integration
        state: >
          {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
          {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
          {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
          {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
          {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
          {{as_datetime(now()) > as_datetime(start_time_lowest_price) and as_datetime(now()) < as_datetime(end_time_lowest_price) and (max_price - min_price) > states('input_number.tibber_cheap_energy_min_price_difference') | float(default=0) }}
        attributes:
          min_price: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {{min_price}}
          max_price: >
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {{max_price}}
          price_range: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {{max_price - min_price}}
          start_time: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
            {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
            {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
            {{start_time_lowest_price}}
          end_time: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
            {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
            {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
            {{end_time_lowest_price}}            
          prices: >
            {{action_response["prices"]["Zuhause"] | list}}

That binary_sensor has a few attributes that i need for my dashboard:

  • min_price => the minimum price in the fetched time-span (in €, so 0,32 means 32 Cents)
  • max_price => the maximum price in the fetched time-span (in €, so 0,40 means 40 Cents)
  • price_range => the difference between the min_price and the max_price (in €, so 0,08 means 8 Cents)
  • start_time => the start-time of the cheapest energy-hour
  • end_time => the end-time of the cheapest energy-hour
  • prices => just for information: the start_times, prices and price_levels in the time-span
  1. a group-helper for binary_sensors that is ON when ALL of the above binary_sensors are on and OFF if at least one of the above binary_sensors is OFF. I set this up via UI like this:

    It is important to toggle “all entities” on for the disrciped behavior

Charging from grid automation:
With alle the above information i made an automation the switches on the “load from grid”-switch from my home-battery-integration if the group_helper-binary_sensor is ON.

alias: Tibber - Speicher günstig laden
description: ""
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.tibber_netzladen_schalter
    to: "on"
    for:
      hours: 0
      minutes: 1
      seconds: 0
    id: Netzladen an
  - trigger: state
    entity_id:
      - binary_sensor.tibber_netzladen_schalter
    to: "off"
    for:
      hours: 0
      minutes: 1
      seconds: 0
    id: Netzladen aus
conditions: []
actions:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - Netzladen an
          - condition: state
            entity_id: switch.senec_safe_charge
            state: "off"
        sequence:
          - action: switch.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: switch.senec_safe_charge
      - conditions:
          - condition: trigger
            id:
              - Netzladen aus
          - condition: state
            entity_id: switch.senec_safe_charge
            state: "on"
        sequence:
          - action: switch.turn_off
            metadata: {}
            data: {}
            target:
              entity_id: switch.senec_safe_charge
mode: single

And for my dashboard i made an mushroom-template-card that shows my if and when the charge from grid will happen:
image

type: custom:mushroom-template-card
primary: >-
  {% if states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'on' %}
    Speicher wird aus dem Netz geladen
  {% elif states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'off' and
  state_attr(entity,'price_range') | float(default=0) >
  states('input_number.tibber_cheap_energy_min_price_difference') %}
    Speicher wird nicht aus dem Netz geladen
  {% elif state_attr(entity,'price_range') | float(default=0) <
  states('input_number.tibber_cheap_energy_min_price_difference') |
  float(default=0) %}
    Keine Netzladung in den nächsten {{states('input_number.tibber_cheap_energy_prices_hours') | int(default=0)}} Stunden
  {% else %}
    Geplante Speicherladung ab {{as_datetime(state_attr(entity,'start_time')).strftime('%-H:%M')}} Uhr
  {% endif %}
secondary: >-
  {% if states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'on' %}
    Der Speicher wird gerade aus dem Netz geladen, weil der aktuelle Preis von {{(state_attr(entity,'min_price') | float(default=0) * 100) | round(1)}} Cent um {{(state_attr(entity,'price_range') | float(default=0) * 100) | round(1)}} Cent unter dem Maximalpreis liegt.
  {% elif states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'off' and
  state_attr(entity,'price_range') | float(default=0) >
  states('input_number.tibber_cheap_energy_min_price_difference') %}
    Der Speicher wird nicht aus dem Netz geladen obwohl die Konditionen günstig sind, weil Überschüsse aus der Photovoltaikanlage vorliegen oder erwartet werden.
  {% elif state_attr(entity,'price_range') | float(default=0) <
  states('input_number.tibber_cheap_energy_min_price_difference') |
  float(default=0) %}
    Die Preisdifferenz zwischen dem niedrigsten und höchsten Preis beträgt nur {{(state_attr(entity,'price_range') | float(default=0) * 100) | round(1)}} Cent. Aus dem Netz geladen wird erst ab {{(states('input_number.tibber_cheap_energy_min_price_difference') | float(default=0) * 100) | round(1)}} Cent.
  {% else %}
    Der Speicher wird von {{as_datetime(state_attr(entity,'start_time')).strftime('%-H:%M')}} bis {{as_datetime(state_attr(entity,'end_time')).strftime('%-H:%M')}} Uhr zu einem Preis von {{(state_attr(entity,'min_price') | float(default=0) * 100) | round(1)}} Cent aus dem Netz geladen. Die Preisdifferenz zum Maximalpreis beträgt {{(state_attr(entity,'price_range') | float(default=0) * 100) | round(1)}} Cent.
  {% endif %}
icon: mdi:home-battery
entity: binary_sensor.tibber_next_good_grid_charging_time
grid_options:
  columns: full
multiline_secondary: true
badge_icon: mdi:transmission-tower
badge_color: >-
  {% if states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'on' %}
    red
  {% elif states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'off' and
  state_attr(entity,'price_range') | float(default=0) >
  states('input_number.tibber_cheap_energy_min_price_difference') %}
    grey
  {% elif state_attr(entity,'price_range') | float(default=0) <
  states('input_number.tibber_cheap_energy_min_price_difference') |
  float(default=0) %}
    grey
  {% else %}
    orange
  {% endif %}
icon_color: >-
  {% if states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'on' %}
    red
  {% elif states(entity) == 'on' and
  states('binary_sensor.tibber_netzladen_schalter') == 'off' and
  state_attr(entity,'price_range') | float(default=0) >
  states('input_number.tibber_cheap_energy_min_price_difference') %}
    grey
  {% elif state_attr(entity,'price_range') | float(default=0) <
  states('input_number.tibber_cheap_energy_min_price_difference') |
  float(default=0) %}
    grey
  {% else %}
    orange
  {% endif %}

2 Likes

Very nice thread with awesome ideas. Thanks!

My issue is that the colors in the charts in summer (mostly green) are different than in winter (mostly orange/red).

What i’d like to see is the apexchart for todays electricity price being colored based on this:

Now, apexchart only accepts hardcoded integers as treshold value so the above values need a template to return this integer.
This is where my knowledge falls short. Has someone else achieved this?

I also found this topic where something similar is achieved.

Edit Decided to take the approach as mentioned in the linked topic.

afbeelding

Cheers.

Is there a problem with the Tibber integration?

image


Everything works as usual here.

I have the same issue since update to 2024.11 today… but i read the prices directly via rest-api also, there is everything ok…

Hi, can someone tell me where or how I have to put this sensor on?

template:
  - trigger:
      - platform: time_pattern
        hours: "/1"
      - platform: homeassistant
        event: start
    action:
      - service: tibber.get_prices
        data:
          start: "{{ (now()).strftime('%Y-%m-%d %H:') + '00:00' }}"
          end: "{{ (now() + timedelta(hours=states('input_number.tibber_cheap_energy_prices_hours') | int(default=24))).strftime('%Y-%m-%d %H:%M:%S') }}"
        response_variable: action_response
    binary_sensor:
      - unique_id: tibber_next_good_grid_charging_time
        name: tibber_next_good_grid_charging_time
# replace "Zuhause" with the name of your "home" in the tibber-integration
        state: >
          {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
          {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
          {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
          {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
          {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
          {{as_datetime(now()) > as_datetime(start_time_lowest_price) and as_datetime(now()) < as_datetime(end_time_lowest_price) and (max_price - min_price) > states('input_number.tibber_cheap_energy_min_price_difference') | float(default=0) }}
        attributes:
          min_price: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {{min_price}}
          max_price: >
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {{max_price}}
          price_range: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {{max_price - min_price}}
          start_time: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
            {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
            {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
            {{start_time_lowest_price}}
          end_time: >
            {% set min_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | min | float(default=0) %}
            {% set max_price = action_response["prices"]["Zuhause"] |map(attribute='price') | list | max | float(default=0) %}
            {% set lowest_price_entry = action_response["prices"]["Zuhause"] | min(attribute='price') %}
            {% set start_time_lowest_price = lowest_price_entry["start_time"] %}
            {% set end_time_lowest_price = as_datetime(start_time_lowest_price) + timedelta(hours=1) %}
            {{end_time_lowest_price}}            
          prices: >
            {{action_response["prices"]["Zuhause"] | list}}

i think this from another source :wink:

I got the same issue with the max price, which is an issue for my loading from grid automation.
Any thoughts on how to fix this, more than welcome!

Same Problem here. Configure code in configuration.yaml,
but no values on: tibber_next_good_grid_charging_time

Think trigger don´t work, need to put code in automations.yaml ?

maybe this is the problem? Get no values from the integration :frowning:

Reload the integration

unfortunately without success

Everything works here, as described above. Maybe reinstall the integration?

Reloading did seem to help with me. But after some the time price again is unavailable.

There seems to be a problem:
Github for Tibber

problems upon problems… :weary: