In the meantime, this might be helpful to you to get current EnergyZero prices into HA. It worked for me, though I’m still looking for a way to get all-in prices.
It seems like the the links you send just give the day ahead prices, right? This is what my custom component does as well. You can then just use this integration.
Very interesting, especially the cost modifier template part! Unfortunately, the Energyzero prices are not completely the same as what the ENTSO api shows.
Would you consider to integrating other api’s like Energyzero into this integration? Or should any deviations from the ENTSO data be fixed with the cost modifier?
The idea is that the cost modifyer is for deviations of individual suppliers from the day ahead prices. Getting API’s from all individual energy suppliers is a lot of work, so I am not planning on doing that. I opened a discussion to share modifyer templates:
EnergyZero is not in there yet though.
Just installed the component, and it seems like a massive opportunity for significant savings on energy bills.
Does anyone have any blueprints to share how to actually build automations around the sensor data we get?
Also, it would be quite interesting to have “cheapest x hour(s) slot/interval” available, so tasks consuming electricity for multiple hours (washing machine, EV charging etc.) could be scheduled accordingly. Is this already doable?
I do not have any blueprint I can share, but you can make a template with Tibbers “PriceLevel” using the Entsoe price sensors.
template:
- sensor:
- name: "Prisnivå"
unique_id: "pricelevel"
state: >-
{% if states('sensor.current_electricity_market_price')|float(0.5) >= states('sensor.average_electricity_price_today')|float(0.5) *0.9
and states('sensor.current_electricity_market_price')|float(0.5) < states('sensor.average_electricity_price_today')|float(0.5) *1.15 %}
NORMAL
{% elif states('sensor.current_electricity_market_price')|float(0) >= states('sensor.average_electricity_price_today')|float(0) *0.6
and states('sensor.current_electricity_market_price')|float(0) < states('sensor.average_electricity_price_today')|float(0) *0.9 %}
CHEAP
{% elif states('sensor.current_electricity_market_price')|float(0) < states('sensor.average_electricity_price_today')|float(0) *0.6 %}
VERY_CHEAP
{% elif states('sensor.current_electricity_market_price')|float(0) >= states('sensor.average_electricity_price_today')|float(0) *1.15
and states('sensor.current_electricity_market_price')|float(0) < states('sensor.average_electricity_price_today')|float(0) *1.4 %}
EXPENSIVE
{% elif states('sensor.current_electricity_market_price')|float(0) >= states('sensor.average_electricity_price_today')|float(0) *1.4
and states('sensor.current_electricity_market_price')|float(0) < states('sensor.average_electricity_price_today')|float(0) *2.0 %}
VERY_EXPENSIVE
{% elif states('sensor.current_electricity_market_price')|float(0) >= states('sensor.average_electricity_price_today')|float(0) *2.0 %}
EXTREMELY_EXPENSIVE
{% endif %}
Then you can make a template binary sensor which is ON when the price is expensive and OFF when price is normal or cheap. With this you can make a automation which use the binary sensor to heat when it is off and lower the temperature when it is on.
- binary_sensor:
- name: "Dyr strom"
state: "{{ states('sensor.prisniva') == 'EXPENSIVE' or states('sensor.prisniva') == 'VERY_EXPENSIVE' or states('sensor.prisniva') == 'EXTREMELY_EXPENSIVE' }}"
You can also modify this blueprint which use Tibber price data to use Entsoe price data.
This code for finding the cheapest 1-2 hours and 1-3 hours with Entsoe is from @stigvi from this norwegian forum: https://www.hjemmeautomasjon.no/forums/topic/11102-strømpriser/?do=findComment&comment=106063
- unique_id: billigste_timer_1_2
name: billigste_timer_1_2
state: >-
{% set l=state_attr('sensor.average_electricity_price_today', 'prices')[14:]|sort(attribute='price') %}
{% set t = now() %}
{{ (t >= as_datetime(l[0].time) and t <= as_datetime(l[0].time) + timedelta(hours = 1))
or (t >= as_datetime(l[1].time) and t <= as_datetime(l[1].time) + timedelta(hours = 1)) }}
- unique_id: billigste_timer_1_3
name: billigste_timer_1_3
state: >-
{% set l=state_attr('sensor.average_electricity_price_today', 'prices')[14:]|sort(attribute='price') %}
{% set t = now() %}
{{ (t >= as_datetime(l[0].time) and t <= as_datetime(l[0].time) + timedelta(hours = 1))
or (t >= as_datetime(l[1].time) and t <= as_datetime(l[1].time) + timedelta(hours = 1))
or (t >= as_datetime(l[2].time) and t <= as_datetime(l[2].time) + timedelta(hours = 1)) }}
Hi!
I have previously set up the NordPool Diff sensor in HA (can’t find the link right now) and with the latest version it uses your ENTSO-e integration. I now try to get it up and running.
I have a question regarding the API key. I have requested the key from the transparency platform support team yesterday but haven’t got it yet. Probably it will show up.
But I can’t find where to add the key in the configuration. Can you please share an example? Thanks!
/M
You won’t get the API key from them. You need to setup an account, request activation of api and subsequently you can generate a key in your account details.
Team are really helpful but can take 24 -48 hours to get response.
Ok, thanks!
They have responded and I may now generate an api key in the entso-e platform. But once I have it, where do I paste it into HA? I can’t find anywhere in the HACS integration to insert it. Please advise!
Not sure what you mean by hacs integration. You add the custom repository in hacs, install entsoe, restart HA, in integrations you add entsoe and follow the config flow. There it’s one of the parameters you need to fill. Steps are all on the GitHub page of @JaccoR
Are there seperate sensors for “today” and “tomorrow” ? Id like to make a flow that uses a offset so making a device turn on when the price is cheapest within (x) hours. Where x can be from 20.00 till the next day 08.00 for example. Not sure how to format it yet though, you got any pointers for me?
Thanks for this, great help and resource! Perhaps @JaccoR could look into integrating these custom scripts to the component itself for “out of the box” experience
This would be very helpful, I am not sure if it is only me or everyone, but the example chart card dies after midnight and it stays blank until noon. As Entso-e delivers prices for tomorrow, I am bit baffled where did the data from yesterday (today’s prices) disappear.
Or perhaps I am just lost how the component and Entso-e API works.
you probably only include the “tomorrow” prices? After midnight tomorrow becomes today and the new tomorrow is only available as from 2 or 3 pm. This may be because you refer to a sensor which no longer contains all attributes (this was recently changed in the integration) - below my code which works seamlessly
series:
- entity: sensor.average_electricity_price_today_2
show:
legend_value: false
stroke_width: 2
float_precision: 3
type: column
opacity: 0.3
color: '#1007f0'
data_generator: |
return entity.attributes.prices_today.map((record, index) => {
return [record.time, record.price];
});
- entity: sensor.average_electricity_price_today_2
show:
legend_value: false
stroke_width: 2
float_precision: 3
type: column
opacity: 0.3
color: '#07bef0'
data_generator: |
return entity.attributes.prices_tomorrow.map((record, index) => {
return [record.time, record.price];
});
Thanks, I’ll test this and report back
Edit: Worked great! Will update open issue on GH accordingly
With the help of several examples already shared on this forum my attempt at a sensor which provides a True / False output on the upcoming 3 cheapest hours; this will be rolling and looks at all available data (today and tomorrow) as from the current hour. Purpose is to automate charging during the cheapest hours; I added state attributes with the time of the cheapest hours. In my automation I assess the time needed to charge the car (Target SoC - Actual SoC times total battery size divided by charging speed). If >2 I charge during all 3 hours, >1 during the 2 cheapest hours etc.
Intention is to make this dynamic depending on the estimated charge time required (eg if 4 hours are needed then add a 4th hour) and prices (top up to 90 or 100% if tariffs are ultra low. Tried to implement the former with a for loop but failed so far. Presumably there are more efficiencies to be added; any suggestions are much appreciated.
Not tested in real life, will do so during the coming night and adjust where needed.
cheapest_3_hours:
friendly_name: cheapest_3hours
value_template: >-
{% set t = now().hour %}
{%if (state_attr('sensor.average_electricity_price_today', 'prices_tomorrow'))!=[]%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices')[t:60]|sort(attribute='price')%}
{%else%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices_today')[t:60]|sort(attribute='price')%}
{%endif%}
{% set t = now() %}
{{ (t >= as_datetime(l[0].time) and t <= as_datetime(l[0].time) + timedelta(hours = 1))
or (t >= as_datetime(l[1].time) and t <= as_datetime(l[1].time) + timedelta(hours = 1))
or (t >= as_datetime(l[2].time) and t <= as_datetime(l[2].time) + timedelta(hours = 1)) }}
attribute_templates:
hour1: >
{% set t = now().hour %}
{%if (state_attr('sensor.average_electricity_price_today', 'prices_tomorrow'))!=[]%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices')[t:60]|sort(attribute='price')%}
{%else%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices_today')[t:60]|sort(attribute='price')%}
{%endif%}
{{ (as_datetime(l[0].time)) }}
hour2: >
{% set t = now().hour %}
{%if (state_attr('sensor.average_electricity_price_today', 'prices_tomorrow'))!=[]%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices')[t:60]|sort(attribute='price')%}
{%else%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices_today')[t:60]|sort(attribute='price')%}
{%endif%}
{{ (as_datetime(l[1].time)) }}
hour3: >
{% set t = now().hour %}
{%if (state_attr('sensor.average_electricity_price_today', 'prices_tomorrow'))!=[]%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices')[t:60]|sort(attribute='price')%}
{%else%}
{% set l = state_attr('sensor.average_electricity_price_today', 'prices_today')[t:60]|sort(attribute='price')%}
{%endif%}
{{ (as_datetime(l[2].time)) }}
My dynamic prices chart is starting to look neat using apexcarts:
Price today & tomorrow on top of each other. Showing both the grid and injection prices using two entities.
I’m still looking for a way to make the header numbers a bit smaller, any tips are welcome!
Code:
type: custom:apexcharts-card
graph_span: 24h
span:
start: day
all_series_config:
stroke_width: 1
show:
in_brush: true
now:
show: true
label: Nu
header:
show: true
title: Electriciteitsprijzen - afname (€/kwh)
show_states: true
standard_format: true
colorize_states: true
chart_type: line
series:
- entity: sensor.average_electricity_price_today
name: Prijs nu
float_precision: 2
type: column
opacity: 1
data_generator: |
return entity.attributes.prices_today.map((record, index) => {
return [new Date(record.time).getTime(), record.price];
});
color: '#4169E1'
show:
legend_value: false
in_header: before_now
- entity: sensor.average_electricity_price_today
name: Zelfde tijd morgen
float_precision: 2
type: column
opacity: 0.8
color: '#00BFFF'
data_generator: |
return entity.attributes.prices_tomorrow.map((record, index) => {
return [new Date(record.time).getTime()-86400000, record.price];
});
show:
legend_value: false
in_header: before_now
- entity: sensor.injectie_average_electricity_price_today
name: Injectie nu
opacity: 1
float_precision: 2
type: column
color: '#006400'
data_generator: |
return entity.attributes.prices_today.map((record, index) => {
return [new Date(record.time).getTime(), record.price];
});
show:
legend_value: false
in_header: before_now
- entity: sensor.injectie_average_electricity_price_today
name: Injectie morgen
float_precision: 2
type: column
color: '#9ACD32'
opacity: 0.8
data_generator: |
return entity.attributes.prices_tomorrow.map((record, index) => {
return [new Date(record.time).getTime()-86400000, record.price];
});
show:
legend_value: false
in_header: before_now
apex_config:
datalabels:
enabled: true
yaxis:
opposite: false
reversed: false
logarithmic: false
decimalsInFloat: 3
min: 0
labels:
show: true
tooltip:
enabled: true
crosshairs:
show: true
xaxis:
labels:
show: true
rotate: -45
I made it work for me in node-red, if anyones interested below is my flow for my dryer.
[{"id":"be5bf9ea2dbab7ee","type":"ha-entity","z":"69ddabec5f0865c0","name":"droger_prijs","server":"c358b7f9.9f5e18","version":2,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"droger_prijs"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"€"},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"state":"price","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1250,"y":220,"wires":[[]]},{"id":"899cfa6862a23187","type":"function","z":"69ddabec5f0865c0","name":"filter","func":"// Set the start and end times for the filter\nvar startTime = msg.starttime;\nvar endTime = msg.endtime;\n\n// Filter the array and return only the items that are between the start and end times\nmsg.payload = msg.data.attributes.prices.filter(function(object) {\n return object.time >= startTime && object.time <= endTime;\n});\n\n// Return the filtered array\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":160,"wires":[["501a717164a4dcb2"]]},{"id":"e8178a02ee68eff3","type":"api-current-state","z":"69ddabec5f0865c0","name":"anwb prijzen","server":"c358b7f9.9f5e18","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.anwb_average_electricity_price_today","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":690,"y":160,"wires":[["899cfa6862a23187"]]},{"id":"5216460e14b19bfd","type":"function","z":"69ddabec5f0865c0","name":"add","func":"const offset = msg.offset;\nfunction addHours(numOfHours, date = new Date()) {\n date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000);\n\n return date;\n}\n\n// payload = tijd+() \nmsg.payload = addHours(msg.offset);\nmsg.date=new Date ()\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":270,"y":160,"wires":[["356cd842a84cff14"]]},{"id":"356cd842a84cff14","type":"moment","z":"69ddabec5f0865c0","name":"endtime","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":"0","adjType":"hours","adjDir":"add","format":"YYYY-MM-DD HH:00:00+01:00","locale":"NL","output":"endtime","outputType":"msg","outTz":"Europe/Amsterdam","x":400,"y":160,"wires":[["defb11b4c94fc552"]]},{"id":"defb11b4c94fc552","type":"moment","z":"69ddabec5f0865c0","name":"starttime","topic":"","input":"date","inputType":"msg","inTz":"ETC/UTC","adjAmount":"1","adjType":"hours","adjDir":"add","format":"YYYY-MM-DD HH:00:00+01:00","locale":"NL","output":"starttime","outputType":"msg","outTz":"Europe/Amsterdam","x":540,"y":160,"wires":[["e8178a02ee68eff3"]]},{"id":"501a717164a4dcb2","type":"sort","z":"69ddabec5f0865c0","name":"","order":"ascending","as_num":true,"target":"payload","targetType":"msg","msgKey":"price","msgKeyType":"jsonata","seqKey":"payload","seqKeyType":"msg","x":950,"y":160,"wires":[["efc9c940ab56296e","72d9907fe8ed6346"]]},{"id":"efc9c940ab56296e","type":"function","z":"69ddabec5f0865c0","name":"Prijs","func":"msg.price = msg.payload[0].price.toFixed(2);\nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1090,"y":200,"wires":[["be5bf9ea2dbab7ee"]]},{"id":"ac78c233b81c0884","type":"api-call-service","z":"69ddabec5f0865c0","name":"Set starttime Droger","server":"c358b7f9.9f5e18","version":5,"debugenabled":false,"domain":"input_datetime","service":"set_datetime","areaId":[],"deviceId":[],"entityId":["input_datetime.droger_timer"],"data":"{\"datetime\":\"{{payload}}\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1260,"y":160,"wires":[[]]},{"id":"72d9907fe8ed6346","type":"function","z":"69ddabec5f0865c0","name":"Tijd","func":"msg.payload = msg.payload[0].time; \nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1090,"y":160,"wires":[["ac78c233b81c0884","5f6f62984b4062a1"]]},{"id":"ca7af94dcf96ba9a","type":"server-state-changed","z":"69ddabec5f0865c0","name":"droger 3u","server":"c358b7f9.9f5e18","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_button.droger_3uur","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"offset","propertyType":"msg","value":"3.0","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":120,"y":80,"wires":[["5216460e14b19bfd"]]},{"id":"bf6eed3ac571e0bb","type":"server-state-changed","z":"69ddabec5f0865c0","name":"droger 6u","server":"c358b7f9.9f5e18","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_button.droger_6uur","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"offset","propertyType":"msg","value":"6.0","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":120,"y":120,"wires":[["5216460e14b19bfd"]]},{"id":"989f04a4bb36b66d","type":"server-state-changed","z":"69ddabec5f0865c0","name":"droger 12u","server":"c358b7f9.9f5e18","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_button.droger_12uur","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"offset","propertyType":"msg","value":"12.0","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":120,"y":160,"wires":[["5216460e14b19bfd"]]},{"id":"200c028aa8282cdc","type":"server-state-changed","z":"69ddabec5f0865c0","name":"droger 24u","server":"c358b7f9.9f5e18","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_button.droger_24uur","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"offset","propertyType":"msg","value":"24.0","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":120,"y":200,"wires":[["5216460e14b19bfd"]]},{"id":"577279dfe84ff6b4","type":"api-call-service","z":"69ddabec5f0865c0","name":"cancel starttime Droger","server":"c358b7f9.9f5e18","version":5,"debugenabled":false,"domain":"input_datetime","service":"set_datetime","areaId":[],"deviceId":[],"entityId":["input_datetime.droger_timer"],"data":"{\"datetime\":\"{{payload}}\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1090,"y":280,"wires":[[]]},{"id":"2cab19150f9faee9","type":"server-state-changed","z":"69ddabec5f0865c0","name":"Cancel droger timer","server":"c358b7f9.9f5e18","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_button.droger_time_cancel","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"1111-11-11 11:11:11","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":830,"y":220,"wires":[["577279dfe84ff6b4","f2f68b9df664ed0e"]]},{"id":"f2f68b9df664ed0e","type":"function","z":"69ddabec5f0865c0","name":"Prijs 0 (cancel)","func":"msg.price = 0;\nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1060,"y":240,"wires":[["be5bf9ea2dbab7ee"]]},{"id":"5f6f62984b4062a1","type":"debug","z":"69ddabec5f0865c0","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1590,"y":120,"wires":[]},{"id":"c358b7f9.9f5e18","type":"server","name":"Home Assistant","version":1,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]
I really like your chart and I’m was looking to build something alike (but I think I’ll just stick with yours :-))
A question is where did you get the information (sensor) for the injection-rates?
I can’t seem to find it in entsoe?