Charging Tesla with excess power from solar panels

try removing the quotes around ā€œ30ā€ (seconds) so itā€™s a number and not text

Sorry, I should have explained that the problem is around the value statement:

value: {{ (states.number.t_ray_charging_amps.state | int ) + 1 }}

If I put in a number eg. like this:

value: 5

the automation works perfectly. But with the curly bracket statement, I get an error message:

Message malformed: expected float for dictionary value @ data['value']

Any help is greatly appreciated.

1 Like

UPDATE: SOLUTION FOUND
Action statement changed to this:

action:
  - service: number.set_value
    data_template:
      entity_id: number.t_ray_charging_amps
      value: >-
        {% set current_amps = states('number.t_ray_charging_amps') | int %} {{
        current_amps + 1 }}

Now it works.

Hi! Iā€™m looking to create an automation to do the same thing.

A couple of questions:

  1. I think your automation will constantly adjust 1 A up or down every 30 seconds. Currently there is no option for it to leave the charging current as it is.

  2. The Tesla app has a minimum set charging current of 5 A. Does the API allow it to go lower? Edit: it does! I thought you had to do a minimum of 5 A charging. Thatā€™s cool!

1 Like

Alright, just played around with it for a bit.

Seeing the current to 0 (zero) amps and turning it on seems to draw around 400-500 Watts, but Iā€™m not sure it charges the car. I think it just powers on the onboard charger. So I think the efficiency will be very low at low amps.

My electrical bill is net metering per hour (if import == export within a clock hour, my bill will be zero), so I might want to do things in bursts of 5A for low solar surplus, if that increases charging efficiency.

I will post my automation here when I get it woeking, either with or without burst charging.

Cheers!

True. I just had troubles to make the value statement (+/- amps) working. Now I start building the complete set of triggers, conditions etc. around the +/- amp function.

Also agree on the probably low efficiency at low amps, but want to try it and see how it works.

I will post as I progress and appreciate your thoughts.

Thanks.

Hello @Finnray

I tried your automation, and it seems awesomeā€¦ I just cannot make it work.

Can you or anyone else help me?

Be aware that im not very skilled at this stuff.

YALM from automation (Im not getting any error):

alias: Tesla Charging
description: ""
trigger:
  - platform: time_pattern
    minutes: "2"
condition:
  - condition: sun
    before: sunset
    after: sunrise
  - type: is_plugged_in
    condition: device
    device_id: 4ebe9b1fbd57c840e4235fbb8af7cbbb
    entity_id: binary_sensor.charger
    domain: binary_sensor
action:
  - if:
      - condition: numeric_state
        entity_id: sensor.omcsbln02t_export_to_grid
        above: 0.6
    then:
      - service: input_number.set_value
        entity_id: number.charging_amps
        data_template:
          value: >-
            {% set current_amps = states('number.charging_amps') | int %} {{
            current_amps + 1 }}
    else:
      - service: input_number.set_value
        entity_id: number.charging_amps
        data_template:
          value: >-
            {% set current_amps = states('number.charging_amps') | int %} {{
            current_amps - 1 }}
mode: single

Currently Iā€™m charging my car with 6 amps. And ā€œsensor.omcsbln02t_export_to_gridā€ is below 0.6 so it should change the amps to 5, which it actually seems like its trying, but nothing happens.

Log from Trace:

Executed: April 9, 2023 at 10:35:56
Result:
params:
  domain: input_number
  service: set_value
  service_data:
    value: 5
    entity_id:
      - number.charging_amps
  target:
    entity_id:
      - number.charging_amps
running_script: false
limit: 10

There is something in value: called ā€œcurrent_ampsā€. Iā€™m not sure what this value/sensor is, and maybe this could be the issue. I just thought that as soon as it gives the right value, it would change number.charging_amps to this value?

Thanks a lot

I got it working!

I think the problem was

input_number.set_value

was changed to:

number.set_value

Thanks :slight_smile:

1 Like

Glad to hear that you got it working Karsten.

Actually, I also got it working and have since refined it quite a bit. Here is the code that I use today:

alias: T-Ray solar charging
description: ""
trigger:
  # Time interval for triggering set
  - platform: time_pattern
    seconds: "30"
condition:
  # Continue if T-Ray is plugged in charger (or exit)  
  - type: is_plugged_in
    condition: device
    device_id: b4929c45bca6cfb24b68e22f7f0f9977
    entity_id: binary_sensor.t_ray_charger
    domain: binary_sensor
  # Continue if T-Ray SOC is below 1% of charge limit (or exit)      
  - condition: template
    value_template: >-
      {{(states('number.t_ray_charge_limit') | int -
      states('sensor.t_ray_battery') | int) > 1}}
action:
  # Continue if more than 688w EXPORT power is available (else)  
  - if:
      - type: is_power
        condition: device
        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
        entity_id: sensor.pow_k_po
        domain: sensor
        above: 688
    then:
      # Turn ON charger if it is OFF and continue (or just continue)
      - if:
          - condition: device
            type: is_off
            device_id: b4929c45bca6cfb24b68e22f7f0f9977
            entity_id: switch.t_ray_charger
            domain: switch
        then:
          - type: turn_on
            device_id: b4929c45bca6cfb24b68e22f7f0f9977
            entity_id: switch.t_ray_charger
            domain: switch
      # Choose number of charging amps to add relative to current EXPORT power  (Note: If current EXPORT power is below 688w -> no change)
      - choose:
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 5504
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 8 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 4816
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 7 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 4128
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 6 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 3440
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 5 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 2752
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 4 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 2064
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 3 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 1376
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 2 }}
          - conditions:
              - type: is_power
                condition: device
                device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                entity_id: sensor.pow_k_po
                domain: sensor
                above: 688
            sequence:
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') |
                    int %} {{ current_amps + 1 }}
    else:
      # Check if power is being imported
      - if:
          - type: is_power
            condition: device
            device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
            entity_id: sensor.pow_k_p
            domain: sensor
            above: 0
        then:
          # Turn OFF charger if power is being imported, but current charging power is below 550w and exit
          - if:
              - type: is_power
                condition: device
                device_id: 3efa4551c778fefe170f4c0c7b4dad6f
                entity_id: sensor.go_echarger_110504_nrg_12
                domain: sensor
                below: 550
            then:
              - type: turn_off
                device_id: b4929c45bca6cfb24b68e22f7f0f9977
                entity_id: switch.t_ray_charger
                domain: switch
            else:
              # Choose number of charging amps to subtract relative to current IMPORT power (Note: If current IMPORT power is below 688w -> no change)
              - choose:
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 5504
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 8 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 4816
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 7 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 4128
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 6 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 3440
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 5 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 2752
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 4 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 2064
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 3 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 1376
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 2 }}
                  - conditions:
                      - type: is_power
                        condition: device
                        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
                        entity_id: sensor.pow_k_p
                        domain: sensor
                        above: 688
                    sequence:
                      - service: number.set_value
                        data_template:
                          entity_id: number.t_ray_charging_amps
                          value: >-
                            {% set current_amps =
                            states('number.t_ray_charging_amps') | int %} {{
                            current_amps - 1 }}
mode: single

Hi @Finnray

I wondered why you made the long list of conditions. Is there any obvious reason that you couldnā€™t just calculate the number you want to adjust the charging current with?
E.g:

      value: >-
            {{ states('number.charging_amps') | int - (states('sensor.active_import_average_5_min')|int / 688) | round(0,'floor') }}

Then you could make the automation much simpler.

And another thing: Did you choose the 30 seconds interval for some specific reason?
I donā€™t have a better value, but I choose every 5 minutes and made the export value a 5 minutes moving average.
Just because I thought it was too often to adjust the charging current more often than this. But I have no strong considerations behind my choice.

Hi @vester,

Thanks for your input. Calculating the charging current adjustment is definitely more rational. I just got down another avenue and blindsighted on that obvious improvement to the code.

The 30 second interval is because I want to adjust close to real time, so that any change in solar production (for instance a cloud passing) and house consumption (for instance a thermostat turning on the electric heating in my garage) is (almost) immediately reflected in the charging amps. Thus minimizing the import of power from the grid.

Here is my updated code:

alias: T-Ray solar charging
description: ""
trigger:
  # Time interval for triggering set
  - platform: time_pattern
    seconds: "30"
condition:
  # Continue if T-Ray is plugged in charger (or exit)  
  - type: is_plugged_in
    condition: device
    device_id: b4929c45bca6cfb24b68e22f7f0f9977
    entity_id: binary_sensor.t_ray_charger
    domain: binary_sensor
  # Continue if T-Ray SOC is below 1% of charge limit (or exit)      
  - condition: template
    value_template: >-
      {{(states('number.t_ray_charge_limit') | int -
      states('sensor.t_ray_battery') | int) > 1}}
action:
  # Continue if more than 688w EXPORT power is available (else)  
  - if:
      - type: is_power
        condition: device
        device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
        entity_id: sensor.pow_k_po
        domain: sensor
        above: 688
    then:
      # Turn ON charger if it is OFF and continue (or just continue)
      - if:
          - condition: device
            type: is_off
            device_id: b4929c45bca6cfb24b68e22f7f0f9977
            entity_id: switch.t_ray_charger
            domain: switch
        then:
          - type: turn_on
            device_id: b4929c45bca6cfb24b68e22f7f0f9977
            entity_id: switch.t_ray_charger
            domain: switch
      # Calculate number of charging amps to add relative to current EXPORT power  (Note: If current EXPORT power is below 688w -> no change)
      - service: number.set_value
        data_template:
          entity_id: number.t_ray_charging_amps
          value: >-
            {% set current_amps = states('number.t_ray_charging_amps') | int %}
            {% set add_amps = (states('sensor.pow_k_po') | int / 688) | round(0,'floor') %}
            {{ current_amps + add_amps }}
    else:
      # Check if power is being imported
      - if:
          - type: is_power
            condition: device
            device_id: 5ab015508a4f0ce2f17d992b8f68c3f0
            entity_id: sensor.pow_k_p
            domain: sensor
            above: 0
        then:
          # Turn OFF charger if power is being imported, but current charging power is below 550w and exit
          - if:
              - type: is_power
                condition: device
                device_id: 3efa4551c778fefe170f4c0c7b4dad6f
                entity_id: sensor.go_echarger_110504_nrg_12
                domain: sensor
                below: 550
            then:
              - type: turn_off
                device_id: b4929c45bca6cfb24b68e22f7f0f9977
                entity_id: switch.t_ray_charger
                domain: switch
            else:
              # Calculate number of charging amps to subtract relative to current IMPORT power (Note: If current IMPORT power is below 688w -> no change)
              - service: number.set_value
                data_template:
                  entity_id: number.t_ray_charging_amps
                  value: >-
                    {% set current_amps = states('number.t_ray_charging_amps') | int %}
                    {% set sub_amps = (states('sensor.pow_k_p') | int / 688) | round(0,'floor') %}
                    {{ current_amps - sub_amps }}
mode: single

3 Likes

Hi there,
Many thanks for all your work. I am a full novice to coding :rofl:
Got enphase solar, a shelly that shows the net total returned to the grid (positive value) or negative value if I import.
Also go 2 tesla home.
Got an issue: amps rapidly rocket to the max (32A) and then stop, but it seem to not adapt.
Here is the code, if you could help :slight_smile:

Alias: Cocotte solar charging
description: ""
trigger:
  - platform: time_pattern
    seconds: "30"
condition:
  - type: is_plugged_in
    condition: device
    device_id: b27fa242ee51ac63d7d7c1d3db0a2b99
    entity_id: binary_sensor.cocotte_charger
    domain: binary_sensor
  - condition: template
    value_template: >-
      {{(states('number.cocotte_charge_limit') | int -
      states('sensor.cocotte_battery') | int) > 1}}
  - condition: device
    device_id: b27fa242ee51ac63d7d7c1d3db0a2b99
    domain: device_tracker
    entity_id: device_tracker.cocotte_location_tracker
    type: is_home
action:
  - if:
      - type: is_power
        condition: device
        device_id: c65fef9bafaa8cbba8bf3eeb386fb2b8
        entity_id: sensor.shellyem_244cab43175e_channel_1_power
        domain: sensor
        above: 688
    then:
      - if:
          - condition: device
            type: is_off
            device_id: b27fa242ee51ac63d7d7c1d3db0a2b99
            entity_id: switch.cocotte_charger
            domain: switch
        then:
          - type: turn_on
            device_id: b27fa242ee51ac63d7d7c1d3db0a2b99
            entity_id: switch.cocotte_charger
            domain: switch
      - service: number.set_value
        data_template:
          entity_id: number.cocotte_charging_amps
          value: >-
            {% set current_amps = states('number.cocotte_charging_amps') | int
            %} {% set add_amps =
            (states('sensor.shellyem_244cab43175e_channel_1_power') | int / 688)
            | round(0,'floor') %} {{ current_amps + add_amps }}
    else:
      - if:
          - type: is_power
            condition: device
            device_id: c65fef9bafaa8cbba8bf3eeb386fb2b8
            entity_id: sensor.shellyem_244cab43175e_channel_1_power
            domain: sensor
            below: 300
        then:
          - if:
              - type: is_power
                condition: device
                device_id: c65fef9bafaa8cbba8bf3eeb386fb2b8
                entity_id: sensor.shellyem_244cab43175e_channel_2_power
                domain: sensor
                below: 6000
            then:
              - type: turn_off
                device_id: b27fa242ee51ac63d7d7c1d3db0a2b99
                entity_id: switch.cocotte_charger
                domain: switch
            else:
              - service: number.set_value
                data_template:
                  entity_id: number.cocotte_charging_amps
                  value: >-
                    {% set current_amps = states('number.cocotte_charging_amps')
                    | int %} {% set sub_amps =
                    (states('sensor.shellyem_244cab43175e_channel_1_power') |
                    int / 688) | round(0,'floor') %} {{ current_amps - sub_amps
                    }}
mode: single
1 Like

OP, is 688 the standard power draw for your house? I keep getting errors saying 17 is out of the range, which it is. 16 is the max ampage for my house.

Iā€™m trying to work out where the math is going wrong.

@finnarne and everyone who is trying this. This is a great idea and thanks for sharing the code. I tried to make it work but i found one issue so far.

My condition always pass to check that Tesla charger in:
type: is_plugged_in
condition: device
device_id: 322d00bd1327c12f2a7be379c7ce50d2 (TeslaCar)
entity_id: 0b60e87852146865b8bd6a6394622eaf (TeslaCar Charger is plugged in)
domain: binary_sensor

My Car polling is on, i had to do force data update in that Tesla Custom Integration plugin. And here are my settings any recommendations?
image

Tesla at home also makes sense, thatā€™s at least what I did :smiling_face:

Hello everyone :slight_smile: Iā€™m new here, Iā€™m trying to figure things out ! (Sorry for my bad English, Iā€™m french !).
I just bought 6 solar panels for 2.88kw (plug and play), and of course, I would like to charge my tesla with the excess power instead of putting it into the grid. Home Assistant seems to be awesome and I canā€™t wait to get it (just bought a raspberryPi). You code is awesome, but how you car is connected, I mean with an EvStation (Mine is 7kw/h) ? Through a connected plug (max 3kw/h) ? Thank you !

@Finnray I could flipping kiss you right now, man!
Your last post showing the updated YAML was just the info I needed to finally rewrite my horrible, clunky set of 24 automations down to 1 automation that calculated the max available charging amps that I can shove into the Model S, without importing anything.
YOU ARE MAGNIFICENT!!!

For years I have struggled, knowing there was a nice way to calculate the amps from the available watts and voltage, but my YAML skills are far too crap to put the maths into practice. Your code is bang on and my car is now merrily lapping up my solar, with the amps being adjusted every 30 seconds.
I made a few little tweaks, but here is the code that works with my Tesla (using @alandtseā€™s nice integration) and my SolarEdge inverter (using @WillCodeForCatsā€™s brilliant Modbus multi integration).

alias: Tesla Solar Charging Automation
description: ""
trigger:
  - platform: time_pattern
    seconds: /30
condition:
  - condition: and
    conditions:
      - condition: sun
        before: sunset
      - condition: sun
        after: sunrise
  - condition: state
    entity_id: device_tracker.nikita_location_tracker
    state: home
  - condition: state
    entity_id: binary_sensor.nikita_charger
    state: "on"
  - condition: template
    value_template: >-
      {{ (states('number.nikita_charge_limit') | int -
      states('sensor.nikita_battery') | int) > 0.1 }}
action:
  - if:
      - condition: numeric_state
        entity_id: sensor.solaredge_m1_ac_power
        above: 250
    then:
      - if:
          - condition: state
            entity_id: switch.nikita_charger
            state: "off"
        then:
          - entity_id: switch.nikita_charger
            action: switch.turn_on
      - data_template:
          entity_id: number.nikita_charging_amps
          value: >-
            {% set current_amps = states('number.nikita_charging_amps') | int %}
            {% set add_amps = (states('sensor.solaredge_m1_ac_power') | int /
            240) | round(0,'floor') %} {{ [current_amps + add_amps, 8] | min }}
        action: number.set_value
    else:
      - if:
          - condition: numeric_state
            entity_id: sensor.solaredge_m1_ac_power
            below: 0
        then:
          - entity_id: switch.nikita_charger
            action: switch.turn_off
        else:
          - data_template:
              entity_id: number.nikita_charging_amps
              value: >-
                {% set current_amps = states('number.nikita_charging_amps') |
                int %} {% set sub_amps = (states('sensor.solaredge_m1_ac_power')
                | int / 240) | round(0,'floor') %} {{ current_amps - sub_amps }}
            action: number.set_value
mode: single

I added some conditions to ensure the automation only runs during daylight hours, and when the car is at home (to limit messing with the car when charging elsewhere), as well as converting to single phase power and capping the amps at 8A (I am running on the UMC plugged into a sketchy garage socket at the moment after having moved house). I will bump those back up to 400V and 32A once my schpanking new 3 phase cabling has gotten the HPWC online. I also trimmed out the device IDs as they were throwing a lot of errors (is that a bad thing to remove them?). I also changed the time pattern from ā€œ30ā€ to /30 as I find that to be a more reliable trigger.

My next improvement will be to take the voltage sensor of the SolarEdge meter/inverter and input that into the amperage calculation so the maths improves as the voltage fluctuates throughout the day, which it does a surprising amount.
For those with knowledge of this with 3 phase SolarEdge inverters, is ā€œsensor.solaredge_m1_ac_voltage_lnā€ the best sensor to use, or do I need to do yet more maths to average out the 3 phase voltages?

Thanks again @Finnray !!!

Ah wait, I celebrated too soon.
When the sun started to go down, instead of gradually reducing the amps, and then stopping when there is zero solar, the automation kept first stopping the charge, and then restarting with too maps amps, and then stopping ad infinitum.
I suspect the ā€˜actionā€™ part of the automation is in the wrong order. Can anyone assist me in rewriting it to first ramp down the amps until there is no solar juice, then to stop the charge?

action:
  - if:
      - condition: numeric_state
        entity_id: sensor.solaredge_m1_ac_power
        above: 250
    then:
      - if:
          - condition: state
            entity_id: switch.nikita_charger
            state: "off"
        then:
          - entity_id: switch.nikita_charger
            action: switch.turn_on
      - data_template:
          entity_id: number.nikita_charging_amps
          value: >-
            {% set current_amps = states('number.nikita_charging_amps') | int %}
            {% set add_amps = (states('sensor.solaredge_m1_ac_power') | int /
            240) | round(0,'floor') %} {{ [current_amps + add_amps, 8] | min }}
        action: number.set_value
    else:
      - if:
          - condition: numeric_state
            entity_id: sensor.solaredge_m1_ac_power
            below: 0
        then:
          - entity_id: switch.nikita_charger
            action: switch.turn_off
        else:
          - data_template:
              entity_id: number.nikita_charging_amps
              value: >-
                {% set current_amps = states('number.nikita_charging_amps') |
                int %} {% set sub_amps = (states('sensor.solaredge_m1_ac_power')
                | int / 240) | round(0,'floor') %} {{ current_amps - sub_amps }}
            action: number.set_value

Charging the car with excess solar power is a really great idea. Your approach has been very inspiring :grinning:. I used a slightly different approach which I like to share here.

Let me know what you think.

My main concern is that with an incremental approach, there is a theoretical risk get into an unrealistic number (e.g. negative or unrealistically high). I am not saying that it is bad; it is just a risk. Therefore I decided to directly estimate the expected power draw of the house and use an input_number that is limited to my electrical setupā€™s amperage (between 5 and 16).

Since the minimal amount of charge current for my Tesla Model Y is 5 Amps (~1100 W), and sometimes the total power would not be enough, I am OK to charge with a mixture of 50% solar and 50% grid energy. I wanted to make sure that I also can accommodate that.
Further, for me it is OK that the routine still draws from or returns to the grid a bit of current. Therefore I decided to update once per 5 minutes.

I therefore splitted up the protocol into four parts/automations:

  • Calculate the Tesla Charge current every 5 minutes (see below)
  • Update the Tesla Charge current into the Tesla
  • Interrupt the charging in case of too little power (less than 1kW)
  • Resume the charging in case of sufficient power (more than 1.2kW)

The last three are quite obvious to implement.

I have several Shelly power meters, so those can be added up + a rest power that I cannot measure and therefore estimated at 350 W. Then I subtract this ā€œhouse powerā€ from the ā€œsolar powerā€ which can go into the Tesla.
I use an input_number helper to store the amount of current. Whenever this value changes and the car is charging at home (+ several other conditions), I update the Teslaā€™s charging current value. This routine is always running every 5 minutes (I could change that if that really has significant impact).
Further, have an input_boolean to turn on ā€œGreen Chargingā€ so I can also use conventional night charging.

Calculate charge current

alias: Tesla Groen charging - caluclate charge current
trigger:
  - platform: time_pattern
    minutes: /5
condition: []
action:
  - variables:
      Phouse: >-
        {{ (states.sensor.boiler_en_wasmachine_power.state | float +
        states.sensor.droger_en_oven_power.state | float +
        states.sensor.werkplek_power.state | float +
        states.sensor.vaatwasser_power.state | float +
        states.sensor.airco_power.state | float) | round(0) }}
      Prest: 350
      Psolar: "{{ -1 * states.sensor.zonnepanelen_power.state | float }}"
      Pcar: "{{ (Psolar - Phouse - Prest) | round (1) }}"
      Voltage: "{{ states.sensor.laadpaal_voltage.state | float }}"
      Icar: "{{ (Pcar / Voltage) | round (0) }}"
  - action: input_number.set_value
    metadata: {}
    data:
      value: "{{ Icar }}"
    target:
      entity_id: input_number.tesla_solar_current
mode: single

Whenever I transfer an action to the car (update current, interrupt/resume charging), I always check the following conditions:

  • Tesla is plugged in
  • Tesla is at home
  • Tesla User present is off
  • Tesla Shift State is Park
  • It is day time
  • Input_boolean Green Charging is ā€œonā€

On my first try, I was able to achieve 93% direct consumption of my solar energy. Hope to hear from you what levels you achieve.

After some head scratching and a lot of trial and error, I now have a working automation that starts the charge, increases amps as solar excess is available, reduces charge amps when power dips into import, and then stops the charge when there are no available amps.

Feel free to use, edit and critique the following automation:

alias: "*Tesla Solar Charging Automation"
description: ""
trigger:
  - platform: time_pattern
    seconds: /30
condition:
  - condition: and
    conditions:
      - condition: sun
        before: sunset
      - condition: sun
        after: sunrise
  - condition: state
    entity_id: device_tracker.nikita_location_tracker
    state: home
  - condition: state
    entity_id: binary_sensor.nikita_charger
    state: "on"
  - condition: template
    value_template: >
      {{ (states('number.nikita_charge_limit') | int -
      states('sensor.nikita_battery') | int) > 0.1 }}
action:
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solaredge_m1_ac_power
            above: 250
        sequence:
          - choose:
              - conditions:
                  - condition: state
                    entity_id: switch.nikita_charger
                    state: "off"
                sequence:
                  - target:
                      entity_id: switch.nikita_charger
                    action: switch.turn_on
                    data: {}
          - data:
              entity_id: number.nikita_charging_amps
              value: >
                {% set current_amps = states('number.nikita_charging_amps') |
                float %} {% set add_amps =
                (states('sensor.solaredge_m1_ac_power') | float / 236) |
                round(0, 'floor') %} {% set new_amps = current_amps + add_amps
                %} {{ [new_amps, 8] | min | float }}
            action: number.set_value
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solaredge_m1_ac_power
            below: 0
        sequence:
          - choose:
              - conditions:
                  - condition: numeric_state
                    entity_id: number.nikita_charging_amps
                    above: 1
                sequence:
                  - data:
                      entity_id: number.nikita_charging_amps
                      value: >
                        {% set current_amps =
                        states('number.nikita_charging_amps') | float %} {% set
                        sub_amps = ((states('sensor.solaredge_m1_ac_power') |
                        float * -1) / 236) | round(0, 'floor') %} {% set
                        new_amps = current_amps - sub_amps %} {{ [new_amps, 1] |
                        max | float }}
                    action: number.set_value
              - conditions:
                  - condition: numeric_state
                    entity_id: number.nikita_charging_amps
                    below: 1.1
                sequence:
                  - target:
                      entity_id: switch.nikita_charger
                    action: switch.turn_off
                    data: {}
mode: single


So far it seems pretty quick to react to changes in solar output (clouds, other loads in the house etc), reliable, and robust.
Enjoy!