evcc-io / evcc

Sonne tanken ☀️🚘
https://evcc.io
MIT License
2.87k stars 543 forks source link

RTE / EDF TEMPO price integration #11040

Open ioniks opened 7 months ago

ioniks commented 7 months ago

Is your feature request related to a problem? Please describe. In France, EDF offers a TEMPO tariff with Blue, White and Red days (with off-peak hour and full hour for all its tariffs) The rates are fixed for periods of 6 months, the color of the day are accessible from the next day (accessible in the morning around 7 or 8 am and confirmed from 11 am) by an API.

Describe the solution you'd like A call to this api to know the color of the current day + the color of the next day would be good with a fixed information of the price in the configuration (which would allow to adapt it for each change)

Additional context Mostly it is up to 16cts the Kwh, however in full hour in red day it is 73 cts the kwh !

Thanks

naltatis commented 7 months ago

Can you provide details for the API?

ioniks commented 7 months ago

Yes.

This repo use it for Home Assistant plugin : https://github.com/hekmon/rtetempo This is the documentation for the API : https://data.rte-france.com/catalog/-/api/doc/user-guide/Tempo+Like+Supply+Contract/1.1

andig commented 7 months ago

Looking at the API it is not clear to me where we can find out what the price for write/blue/red hours are?

ioniks commented 7 months ago

Like i say in the issue, the price is not in the API, just the color of the day. The price is fixed every 6 month and not in API, I proposed to be able to modify it in the configuration for this.

andig commented 7 months ago

@ioniks could you please share an access token for testing with info@evcc.io?

ioniks commented 7 months ago

@andig it's sended

andig commented 7 months ago

Re https://github.com/evcc-io/evcc/pull/11051#issuecomment-1847462131: I no longer understand the price structure. Can you please explain the tariff (again)? What are the colors? Only a price? Or price plus peak time (s)?

ioniks commented 7 months ago

Yes

For each day color there are two prices. off-peak and peak hours.

off-peak hours vary from one person to another in France. For example, I have from 10:38 p.m. to 6:38 a.m. and another person can have 10:48 p.m. to 6:48 a.m.

Actually the price table is : (cts €/kWh) Blue :

White :

Red :

andig commented 7 months ago

Ok. And are the peak/off peak hours the same time for the different colors? Or does each color need its own time table in addition to its own prices? Is the off-peak price identical across all colors?

ioniks commented 7 months ago

Yes off peak are always the same time

andig commented 7 months ago

To summarise:

Correct?

ioniks commented 7 months ago

No :

Actually : Blue off peak price : 10.56 cts € White off peak price : 12.46 cts € Red off peak price : 13.28 cts €

Every peak and off peak price is different for every color (6 different tarifs)

andig commented 7 months ago

Sounds like we should change the config like this:

type: edf-tempo
clientid: ...
clientsecret: ...
zones: // peak zones
  - ...
prices:
  red:
    peak: ...
    offpeak:
  blue:
    peak: ...
    offpeak: ...
  white:
    peak: ...
    offpeak: ...
ioniks commented 7 months ago

Yes, perfect.

Configuration of peak zone is more simple than configuration of off-peak zone.

👍

ioniks commented 7 months ago

hi @andig ,

Litlle update : The peak and off-peak hours for this subscription are the same nationally in France from 6:00 to 22:00 for peak hours and from 22:00 to 6:00 for off-peak hours

Sorry I didn’t get this information ...

But who can the most can the least ;)

ioniks commented 7 months ago

Hi @andig do you need more information ?

oliviermichaud commented 5 months ago

Hi,

Is this feature is operationnal. I don't succeed to implement it on my config. I have an error :

panic: assignment to entry in nil map

goroutine 118 [running]: github.com/evcc-io/evcc/tariff.NewEdfTempoFromConfig(0x4000d3dfe4?) github.com/evcc-io/evcc/tariff/edf-tempo.go:73 +0x504

This is my test config :

tariffs: currency: EUR # (default EUR) grid:

See https://github.com/hekmon/rtetempo to generate api Application on rte-france.com

type: edf-tempo
clientid: xxx
clientsecret: xxx
prices:
  red: 0.3
  blue: 0.3
  white: 0.3

I don't succeed too with this config : prices: red: peak: ... offpeak: blue: peak: ... offpeak: ... white: peak: ... offpeak: ...

Please someone how succeed to configure it can share his config please?

A big Thanks

ioniks commented 5 months ago

@oliviermichaud

This is not finished

oliviermichaud commented 5 months ago

@ioniks

Ok thank you for your fast answer. So i will wait.

For information, the EDF Tempo subscription volume increase a lot in France due to the price of electricity. I’m sure this development interest a lot of people.

Thank you for your Job. Evcc is so nice

MrBLJ commented 3 weeks ago

Hi all. First of all, thanks for the amazing work done on EVCC. Is the price integration feature for EDF tempo still being worked on ?

As mentioned by @oliviermichaud, the service is getting quite popular in France, in particular amongst EV users.

If the feature has been dropped or put on the backlog, I could use some feedback on this idea :

Home assistant provides an integration for EDF tempo, that contains sensors with the current color of the day and next's day color.

We could create template sensors, based on the color values and tariffs (fixed in HA config or using input_numbers), to return the current price (calculated using color, tariff and time since we have peak and offpeak hours) as well as a forecast.

Then, create a custom Tariff as specified in the doc :

tariffs:
  grid:
    type: custom
    price:
      source: http
      uri: ...

For the uri we can use home assistant rest API.

I think the same method could be applied to create a forecast.

What do you think about all this ? Does this sound ok ?

ioniks commented 3 weeks ago

On HA : I have 6 Input with color price HP/HC, when it change on the plugin i update the current price and update it on MQTT.

And EVCC read it from MQTT with this :

tariffs:
  currency: EUR
  grid:
    type: custom
    price: 
      source: mqtt
      topic: solax/grid/price
MrBLJ commented 3 weeks ago

Wow, thanks for this fast answer ! If I understand correctly, you've defined a MQTT sensor that returns the current price based on the color of the day, time, and price set in input numbers (two for each color, so 6 in total).

That sounds way more practical than what I had in mind.

Have you implemented a sensor for the forecast as well ?

ioniks commented 3 weeks ago

Just now price, i think i can't do a forecast price on EVCC with Mqtt source.

But when the price change and i have activated smart cost, the charge begin.

MrBLJ commented 3 weeks ago

Ok this is getting more and more interesting because I think you've achieved something I'm trying to : charge on solar during the day and trigger charge at night when the price is low.

Would you mind detailing your setup ? We can discuss elsewhere if you'd be willing to.

ioniks commented 3 weeks ago

input number in HA :

prix_electricite_actuel:
  name: "Prix électricité Actuel"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "EUR/kWh"  
  min: 0
  max: 5
prix_electricite_rouge_hp:
  name: "Prix électricité Rouge HP"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5
prix_electricite_rouge_hc:
  name: "Prix électricité Rouge HC"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5
prix_electricite_blanc_hp:
  name: "Prix électricité Blanc HP"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5
prix_electricite_blanc_hc:
  name: "Prix électricité Blanc HC"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5
prix_electricite_bleu_hp:
  name: "Prix électricité Bleu HP"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5
prix_electricite_bleu_hc:
  name: "Prix électricité Bleu HC"
  mode: "box"
  icon: "mdi:transmission-tower-export"
  step: 0.01
  unit_of_measurement: "€"  
  min: 0
  max: 5

Update price automation :

alias: "[TEMPO] Update prix courant electricité"
description: ""
trigger:
  - type: turned_on
    platform: device
    device_id: XXXXX
    entity_id: binary_sensor.rte_tempo_heures_creuses
    domain: binary_sensor
  - type: turned_off
    platform: device
    device_id: XXXXX
    entity_id: binary_sensor.rte_tempo_heures_creuses
    domain: binary_sensor
  - platform: state
    entity_id:
      - sensor.rte_tempo_couleur_actuelle
  - platform: homeassistant
    event: start
condition: []
action:
  - service: script.XXXXXXX
    data: {}
mode: single

Script :

alias: "Grid: Energy Meter Selection"
sequence:
  - choose:
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Bleu
          - type: is_on
            condition: device
            device_id: XXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_bleu_hc') }}"
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Bleu
          - type: is_off
            condition: device
            device_id: XXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_bleu_hp') }}"
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Blanc
          - type: is_on
            condition: device
            device_id: XXXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_blanc_hc') }}"
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Blanc
          - type: is_off
            condition: device
            device_id: XXXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_blanc_hp') }}"
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Rouge
          - type: is_on
            condition: device
            device_id: XXXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_rouge_hc') }}"
      - conditions:
          - condition: state
            entity_id: sensor.rte_tempo_couleur_actuelle
            state: Rouge
          - type: is_off
            condition: device
            device_id: XXXXX
            entity_id: binary_sensor.rte_tempo_heures_creuses
            domain: binary_sensor
        sequence:
          - service: input_number.set_value
            data_template:
              entity_id: input_number.prix_electricite_actuel
              value: "{{ states('input_number.prix_electricite_rouge_hp') }}"
mode: single
icon: mdi:script-text

Device_id have to be updated after creating yours. I don't know if device id is not set if it work ^^

You have to update manually in the UI when the tempo price change (you can make an automation to update at a specific date)

ioniks commented 3 weeks ago

And create a automation to update MQTT price with an automation on Price modified :

- service: mqtt.publish
    data:
      topic: solax/grid/price
      payload_template: "{{states('input_number.prix_electricite_actuel')}}"
MrBLJ commented 3 weeks ago

And create a automation to update MQTT price with an automation on Price modified :

- service: mqtt.publish
    data:
      topic: solax/grid/price
      payload_template: "{{states('input_number.prix_electricite_actuel')}}"

Thanks a lot ! This pointed me on the right way and hopefully I get this working soon. I fumbled a little with Jinja to render a forecast as specified in evcc doc. Here's my output from HA sensors :

[
  {
    "start": "2024-07-01T12:52:00Z",
    "end": "2024-07-01T22:00:00Z",
    "price": 0.1894
  },
  {
    "start": "2024-07-01T22:00:00Z",
    "end": "2024-07-02T06:00:00Z",
    "price": 0.1296
  },
  {
    "start": "2024-07-02T06:00:00Z",
    "end": "2024-07-02T22:00:00Z",
    "price": 0.1894
  },
  {
    "start": "2024-07-02T22:00:00Z",
    "end": "2024-07-03T06:00:00Z",
    "price": 0.1296
  }
]

Template code. THIS IS UNTESTED AND NEEDS OPTIMIZATION + CLEANING :

{% set current_color = states('sensor.rte_tempo_couleur_actuelle') %}
{% set next_color = states('sensor.rte_tempo_prochaine_couleur') %}
{% set isOffPeak = is_state('binary_sensor.rte_tempo_heures_creuses', 'on') %}
{% set current_time = as_timestamp(as_local(utcnow())) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ') %}
{% set next_period_sensor_state =  states('sensor.rte_tempo_heures_creuses_changement') %}
{% set next_period = as_timestamp(next_period_sensor_state) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ')  %}
{% set next_color_sensor_date =  states('sensor.rte_tempo_prochaine_couleur_changement') %}

{%- set tempoTariffs = {
    'peak_bleu' : 0.1609,
    'offpeak_bleu' : 0.1296,
    'peak_bleu' : 0.1894 ,
    'offpeak_blanc' : 0.1486,
    'peak_blanc' : 0.1894,
    'peak_rouge' : 0.7562,
    'offpeak_rouge' : 0.156               
 }
-%}

{%- macro tempoTariff(tempoColor, offPeakHours) -%}
    {%- set tariff = '' -%}
    {%- if tempoColor == 'Bleu' -%}
        {%- if offPeakHours -%}
            {%- set tariff = tempoTariffs.offpeak_bleu -%}
        {%- else -%}
            {%- set tariff = tempoTariffs.peak_bleu -%}
        {%- endif -%}
    {%- elif tempoColor == 'Blqnc' %}
        {%- if offPeakHours -%}
            {%- set tariff = tempoTariffs.offpeak_blanc -%}
        {%- else -%}
            {%- set tariff = tempoTariffs.peak_blanc -%}
        {%- endif -%}
    {%- elif tempoColor == 'Rouge' %}
        {%- if offPeakHours -%}
            {%- set tariff = tempoTariffs.offpeak_rouge -%}
        {%- else -%}
            {%- set tariff = tempoTariffs.peak_rouge -%}
        {%- endif -%}
    {%- else -%}
        {%- set tariff = 'unknown' -%}
    {%- endif -%}
    {{ tariff }}
{%- endmacro -%}

{% set forecast_items_count = 2 if next_color != 'unknown' and next_color != '' else 1 %}

{% set forecast = [] %}
{% set forecast = namespace(forecast_items=[])%}

{# Loop to generate forecast items #}
{% for i in range(forecast_items_count) %}
  {% set is_current_color = (i == 0) %}

  {% if is_current_color %}
    {% set color = current_color %}
    {% set start_time = current_time %}
    {% set end_time = next_period %}

    {% set forecast_item = {
      "start": start_time,
      "end": next_period,
      "price": tempoTariff(current_color, isOffPeak) | float
    } %}

    {% set forecast.forecast_items = forecast.forecast_items + [forecast_item] %}

    {% if is_current_color and not isOffPeak %}

        {% set color = current_color %}
        {% set start_time = current_time %}
        {% set end_time = next_period %}

        {% set forecast_item = {
          "start": next_period,
          "end": (as_timestamp(states('sensor.rte_tempo_heures_creuses_changement'))  + 8*3600 ) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ'),
          "price": tempoTariff(current_color, not isOffPeak) | float
        } %}

    {% set forecast.forecast_items = forecast.forecast_items + [forecast_item] %}

    {% endif %}
  {% else %}

  {% set next_color_time = as_timestamp(states('sensor.rte_tempo_prochaine_couleur_changement')) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ')  %}
{% set next_color_peak_start = as_timestamp(next_color_sensor_date) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ')  %}
{% set next_color_peak_end = (as_timestamp(next_color_sensor_date) + 16*3600) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ')  %}
{% set next_color_offpeak_end = (as_timestamp(next_color_sensor_date) + 24*3600) | timestamp_custom('%Y-%m-%dT%H:%M:%SZ')  %}

    {% set color = next_color %}
    {% set start_time = next_time %}

    {% set forecast_item = {
      "start": next_color_peak_start,
      "end": next_color_peak_end,
      "price": tempoTariff(next_color, false) | float
    } %}

    {% set forecast.forecast_items = forecast.forecast_items + [forecast_item] %}

    {% set forecast_item = {
      "start": next_color_peak_end,
      "end": next_color_offpeak_end,
      "price": tempoTariff(next_color, true) | float
    } %}

    {% set forecast.forecast_items = forecast.forecast_items + [forecast_item] %}

  {% endif %}

{% endfor %}

{{forecast.forecast_items}}
ioniks commented 3 weeks ago

Yes this can be useful but i just need price for smart charge. I activated one night at a time, so when i activate it, next day it's deactivated so forecast not needed for me ;)

But thanks to share.