Thank you very much, jonas21 - that helped me so much
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
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
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?
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:
- GitHub - RomRider/apexcharts-card: 📈 A Lovelace card to display advanced graphs and charts based on ApexChartsJS for Home Assistant
- your tibber API key
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).
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":""}]
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.
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
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?
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:
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:
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:
- Tabbed-Card (HACS)
- Apex-Charts-Card (HACS)
- Stack-in-a-Card (HACS)
- Uptime-Card (HACS)
- Mushroom-Card HACS)
- Multiscrape-Integration (HACS)
- Markdown-Card
YAML-Code for the Card and Sensors
- YAML-Code for the Card
- YAML-Code for the Weekly Forecast Card
- Sensors for price preview and price level
- Multiscrape-Sensors for price forecast
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)
Input-Boolean (Helper) that show if the home battery should be charged or not
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
Love it. That’s not far what I’m planning to do.
Gives me a lot of ideas and inspiration. Thank you very much.
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:
Using a calendar to show hourly pricing would be easy: simply plan events for every hour with the pricing info in the title.