jonasbkarlsson / ev_smart_charging

Electric vehicle smart charging for Home Assistant.
MIT License
169 stars 25 forks source link

Load balancing #247

Open tonysprenk opened 7 months ago

tonysprenk commented 7 months ago

Is your feature request related to a problem? Please describe. I have a 32amp home electrical connection and my EV charger allows me to charge at up to 32amps. Of course charging at the full capacity would trip a breaker. I can set the max amps my charger will output within home assistant and I can read the total draw of my electrical system through my smart meter. It would be great if this integration allowed me to set a max draw so that it can set the amps of my EV charger so that the total draw will never be higher than, in my case, 32amps.

Describe the solution you'd like

I can set the max amps my charger will output within home assistant and I can read the total draw of my electrical system through my smart meter. It would be great if this integration allowed me to set a max draw so that it can set the amps of my EV charger so that the total draw will never be higher than, in my case, 32amps. AKA: Load Balancing.

Thanks for the great integration!

RienduPre commented 6 months ago

I would love this too

janneho commented 5 months ago

Something like this. I had this on our old house when we had electric heating.

GO-e charger balancing

RienduPre commented 5 months ago

Exactly like that. This is what I use now as an alternative.

janneho commented 5 months ago

I dont use that script anymore, but if someone is using go-e charger it has now load balancing in charger it self (in newer firmware). You just feed in available current (loa), and it limits current by itself. Also if You have several go-e chargers, it limits total current for all chargers.

RienduPre commented 5 months ago

To be clear. I use a Wallbox Pulsar Plus and not a GO-e, and adjusted to automation to my needs.

corvy commented 1 month ago

I use an automation to dynamically change the charger amps with adjusting either the Tesla integration or the smart charger integration. I use this to stay under (but as close to) the effect tariff configured.

I have one automation first that selects whether to balance the charge amp with the charger (Easee) or via the car (Tesla). After that this script watches and adjust the speed continuously during charging.

alias: El-bil, juster ladefart kontinuerlig
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.eneren_status
    to: charging
  - platform: state
    entity_id:
      - binary_sensor.tibber_pulse_failed
    to: "off"
  - platform: homeassistant
    event: start
condition: []
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 11
      milliseconds: 0
  - if:
      - condition: state
        entity_id: input_boolean.easee_er_master
        state: "off"
    then:
      - service: easee.set_circuit_dynamic_limit
        data:
          current_p1: 40
          current_p2: 40
          current_p3: 40
          time_to_live: 0
          device_id: 8726da93cdfc28a403ccfe002a57b593
  - repeat:
      sequence:
        - sequence:
            - delay:
                hours: 0
                minutes: 0
                seconds: 15
                milliseconds: 0
            - if:
                - condition: state
                  entity_id: input_boolean.easee_er_master
                  state: "on"
              then:
                - service: easee.set_circuit_dynamic_limit
                  data:
                    charger_id: XXXXXXXX
                    current_p1: >
                      {% set entity_new_a = 'number.ev_sc_eneren_charging_speed'
                      %} {% set new_A = states(entity_new_a)|int(default=6) %}
                      {% set P =
                      ((states('input_number.effekttariff_max_kwh')|float -
                      states('sensor.estimated_consumption_current_hour_rykkinnveien_67')|float)
                      * 1000) / 230 %}

                      {% if P|int > 1 %}
                        {% set new_A = new_A + 1 %}
                      {% endif %}

                      {% if P|float < 0 %}
                        {% set new_A = new_A - 1 %}
                      {% endif %}

                      {% if P|int > 4 %}
                        {% set new_A = new_A + 4 %}
                      {% endif %}

                      {% if P|float < -4 %}
                        {% set new_A = new_A + (P // 3)|int %}
                      {% endif %}

                      {% if new_A < 0 %}
                        0
                      {% elif new_A > 40 %}
                        40
                      {% else %}
                        {{ new_A|round(0, 'floor') }}
                      {% endif %}
              else:
                - service: number.set_value
                  target:
                    entity_id: number.lexie_charging_amps
                  data:
                    value: >
                      {% set entity_new_a = 'number.lexie_charging_amps' %} {%
                      set new_A = states(entity_new_a)|int(default=6) %} {% set
                      P = ((states('input_number.effekttariff_max_kwh')|float -
                      states('sensor.estimated_consumption_current_hour_rykkinnveien_67')|float)
                      * 1000) / 230 %}

                      {% if P|int > 1 %}
                        {% set new_A = new_A + 1 %}
                      {% endif %}

                      {% if P|float < 0 %}
                        {% set new_A = new_A - 1 %}
                      {% endif %}

                      {% if P|int > 4 %}
                        {% set new_A = new_A + 4 %}
                      {% endif %}

                      {% if P|float < -4 %}
                        {% set new_A = new_A + (P // 3)|int %}
                      {% endif %}

                      {% if new_A < 0 %}
                        0    {# Tesla min 0A #}
                      {% elif new_A > 24 %}
                        24   {# Tesla maks 24A #}
                      {% else %}
                        {{ new_A|round(0, 'floor') }}
                      {% endif %}
            - delay:
                seconds: >
                  {% if now().minute > 45 or
                  states('binary_sensor.effektbegrenser') == 'on' %}
                    30
                  {% else %}
                    60
                  {% endif %}
      while:
        - condition: state
          entity_id: binary_sensor.tibber_pulse_failed
          state: "off"
        - condition: state
          entity_id: sensor.eneren_status
          state:
            - charging
            - awaiting_start
        - condition: template
          value_template: >-
            {{ not (states('sensor.eneren_status') == 'awaiting_start' and
            states('automation.elbil_planlagt_lading_start') == 'on') }}
          enabled: false
mode: single

Could this be adapted to your use-case?

tonysprenk commented 1 month ago

@corvy this is exactly what I'm looking for but not a star with writing automations like this. Could you help me adapt it to my situation?

I have 1 phase, the current sensor of which is: sensor.current_phase_1 and the variable load (smart EV charger) current regulator is: number.ev_charger_tuya_local_set_charge_current.

The EV charger can be set from 1 to 32 but I have noticed that if there is a sudden change (example: from 32 to 21) the charger get's an error and stops charging.

The sensor that looks if the charging has started is: binary_sensor.leaf1spre_charging the attributes of which are "charging" and "not charging".

The maximum allowable current is 40a.

Thanks for any help!

corvy commented 1 month ago

Ok, so the load on sensor.current_phase_1 must be maximum 32A. Does it report A or Watt? And the charger can be from 1A to 40A but you want it to be as close to 32A as possible. Ergo charger + phase_1 should always be less than 32A? And if it goes above the breaker trips?

Maybe we should aim for 30A to have some headroom. Is this correctly understood?

tonysprenk commented 1 month ago

Almost correct. Load on phase 1 cannot exceed 40 amps (with some headroom already calculated in) and it reads in amps. Charger output amps can be anything between 1 and 32 amps.

corvy commented 1 month ago

Ok I have tried to look at a logic based on what I do myself, but adjust it to your use-case.

description: "Adjust charging current to stay under, but as close to as possible, 40A"
mode: single
trigger:
  - platform: state
    entity_id: binary_sensor.leaf1spre_charging
    to: 'charging'
  - platform: homeassistant
    event: start
condition:
  - condition: state
    entity_id: binary_sensor.leaf1spre_charging
    state: 'charging'
action:
  - repeat:
      sequence:
        - service: input_number.set_value
          data:
            value: >
              {% set current_phase_1 = states('sensor.current_phase_1')|float %}
              {% set max_current = 40 %}
              {% set step = 3 %}
              {% set current_setting = states('number.ev_charger_tuya_local_set_charge_current')|float %}

              {% if current_phase_1 >= max_current %}
                {% set new_current = current_setting - step %}
                {% if new_current < 0 %} 
                  {% set new_current = 0 %}
                {% endif %}
              {% elif current_phase_1 < max_current - step %}
                {% set new_current = current_setting + step %}
                {% if new_current > max_current %}
                  {% set new_current = max_current %}
                {% endif %}
              {% else %}
                {% set new_current = current_setting %}
              {% endif %}

              {{ new_current|round(0, 'floor') }}
          target:
            entity_id: number.ev_charger_tuya_local_set_charge_current
        - delay: '00:00:30'  # Adjust the delay as needed
      while:
        - condition: state
          entity_id: binary_sensor.leaf1spre_charging
          state: 'charging'

Note this:

I have not been able to test this logic, but please test it and see how you get on. Also, you could change the max_current and step to adjust how it works. I have max_current to 40 as you say there is some more headroom. Within the 30 seconds we could boost above 40...

janneho commented 1 month ago

I have bit dirrerent automation for load balancing. Mine was quite same as corvys, but it it was too slow to react with p1 meter (it only reads current every 10 seconds). It would take minute to ramp down 6 amps in 1 amp steps (we have 25 amp fuses, and charger can charge 6-16A). So this calculates amps every time, and it reacts in 10 seconds.

If someone needs inspiration. This is for 3-phase charging, homewizard pi meter and go-e charger. It should be easily adapted for 1 phase and for different chargers etc.

If any phase is under 24A, or over 25A it sets charging current to (25A-measured current+charging current). For example: Measured current 19,8A, charging current 10A -> new charging current 15A, Measured current 28,2A, charging current 15A -> new charging current 11A.

Automation works allways when charger is charging.

You also need one helper (sensor.p1_meter_max_current). This is added as : helpers -> group -> new group "p1_meter_max_current". There add all current measuremets (3 phases). For homewizard p1 those would be "P1 meter current phase 1" (and 2 and 3). Type maximum.

kuva

alias: "Lataustehonsaato"
description: Decrease EV charger amps to avoid overcurrent
trigger:
  - platform: state
    entity_id:
      - sensor.p1_meter_max_current
condition:
  - condition: state
    entity_id: sensor.go_echarger_XXXXXX_car
    state: Charging
    enabled: true
  - condition: or
    conditions:
      - condition: and
        conditions:
          - condition: numeric_state
            entity_id: sensor.p1_meter_max_current
            below: 24
            enabled: true
          - condition: numeric_state
            entity_id: number.go_echarger_XXXXXX_amp
            below: 16
      - condition: and
        conditions:
          - condition: numeric_state
            entity_id: sensor.p1_meter_max_current
            enabled: true
            above: 25
          - condition: numeric_state
            entity_id: number.go_echarger_XXXXXX_amp
            above: 6
    enabled: true
action:
  - if:
      - condition: template
        value_template: >-
          {{ ((((states("number.go_echarger_XXXXXX_amp")|float + 25) -
          (states("sensor.p1_meter_max_current")|float))|round(0, "floor")) < 6
          ) }}
    then:
      - service: number.set_value
        metadata: {}
        data:
          value: "6"
        target:
          entity_id: number.go_echarger_XXXXXX_amp
    else:
      - if:
          - condition: template
            value_template: >-
              {{ ((((states("number.go_echarger_XXXXXX_amp")|float + 25) -
              (states("sensor.p1_meter_max_current")|float))|round(0, "floor"))
              > 16 ) }}
        then:
          - service: number.set_value
            metadata: {}
            data:
              value: "16"
            target:
              entity_id: number.go_echarger_XXXXXX_amp
        else:
          - service: number.set_value
            metadata: {}
            data:
              value: >-
                {{ (((states("number.go_echarger_XXXXXX_amp")|float + 25) -
                (states("sensor.p1_meter_max_current") |float))|round(0,"floor")
                ) }}
            target:
              entity_id: number.go_echarger_XXXXXX_amp
mode: single
max: 3
janneho commented 1 month ago

And corvys script is much ncer formatted, so this is same logic as before but in different way.

{% set min_charge_current = 6 %}
{% set max_charge_current = 16 %}
{% set max_total_current = 25 %}

{% set measured_current = states('sensor.p1_meter_max_current')|float %}
{% set current_charger_setting = states('number.go_echarger_XXXXXX_amp')|float %}

{% set new_current = max_total_current - measured_current + current_charger_setting %}
{% if new_current > max_charge_current %}
  {% set new_current = max_charge_current %}
{% endif %}
{% if new_current < min_charge_current %}
  {% set new_current = min_charge_current %}
{% endif %}

{{ new_current|round(0, 'floor') }}
janneho commented 1 month ago

This propably does not matter in your case, but standard for charging allows minimum charge current 6A. So do not try to go under that, or "anything" can happen. Most chargers do not allow setting charge current under that anyway.

janneho commented 1 month ago

This is my current load balancing automation. It seems to work well. Should be easily adopted to many situations.

Following should be modified for different meters/chargers: min_charge_current = minium charge current charger allows max_charge current = maximum charge current charger allows max_total_current = maximum total current allowed (breaker size) c1, c2 and c3 = mains current measurements (3 phase, in amps) current_charger_setting = EV charger current setting variable

alias: "!Uusi kuromanhallinta"
description: Control EV charger amps to avoid overcurrent
trigger:
  - platform: state
    entity_id:
      - sensor.p1_meter_current_phase_1
      - sensor.p1_meter_current_phase_2
      - sensor.p1_meter_current_phase_3
condition:
  - condition: state
    entity_id: sensor.go_echarger_081917_car
    state: Charging
    enabled: true
action:
  - service: number.set_value
    data:
      value: >
        {% set min_charge_current = 6 %}
        {% set max_charge_current = 16 %}
        {% set max_total_current = 25 %}

        {% set c1 = states('sensor.p1_meter_current_phase_1')|float %}
        {% set c2 = states('sensor.p1_meter_current_phase_2')|float %}
        {% set c3 = states('sensor.p1_meter_current_phase_3')|float %}
        {% set measured_current = [c1, c2, c3] | select('is_number') | map('float') | max | default(none) %}

        {% set current_charger_setting = states('number.go_echarger_xxxxxx_amp')|float %}

        {% set new_current = max_total_current - measured_current + current_charger_setting %}
        {% if new_current >max_charge_current %}
          {% set new_current = max_charge_current %}
        {% endif %}
        {% if new_current < min_charge_current %}
          {% set new_current = min_charge_current %}
        {% endif %}

        {{ new_current|round(0, 'floor') }}
    target:
      entity_id: number.go_echarger_xxxxxx_amp
mode: single
max: 3
tonysprenk commented 1 month ago

It works! I made a few small tweaks. For this test I set the maximum current to 29a. The total current on phase 1 is the top dial and the variable load (EV charger) current is the value at the bottom. The spike in current is my electric hob turning on, the automation handles it well. Thanks for everyone's support! https://github.com/user-attachments/assets/d14bf62c-6658-4e2d-a276-d761dcef8b11