After browsing a couple of solutions for this, I figured out how to make my EV charge on excess solar power production (when available). After automating the process of adjusting the charging power to the current grid power state, I’ve added some functionality to optimize the usage. I’ve tried covering basic usability and some added usability scenario’s. Let me take you through the steps.
Here’s what I started with:
- a smart power meter (using the DSMR integration) that shows the total grid power consumption and production
- a Tesla (using the HACS Tesla integration)
- solar panels feeding power to my home grid
- an iPhone with the Home Assistant companion app
Objective:
Feed excessive solar power to the car when it’s connected at home.
Steps taken:
Although I could have used the ‘power consumption’ directly (as it already is the sum of power production minus power consumption) to start the charging and calculate the amount of power surplus to use for charging, it would be a bit impractical as I would need to incorporate the power consumption value as well when re-calculating the power draw while charing. To avoid this, I had decided on creating a single ‘grid usage’ sensor. It looks like this:
- platform: template
sensors:
power_grid_usage:
friendly_name: "Power Grid Usage"
unit_of_measurement: "W"
value_template: >
{% set power_production_w = states('sensor.power_production') | float * 1000 %}
{% set power_consumption_w = states('sensor.power_consumption') | float * 1000 %}
{% if power_production_w >= 0 and power_consumption_w >= 0 %}
{{ (power_production_w - power_consumption_w) }}
{% elif power_production_w >= 0 %}
{{ power_production_w }}
{% else %}
{{ -1 * power_consumption_w }}
{% endif %}
Next, I needed an automation that would start the charging when there is a grid surplus, adjusts the charging rate for varying power production and stops the charging when there is no surplus available. I’ve calculated it using 240 watts increments. Here’s what that automation looks like:
alias: "[Tesla] Charge on solar power"
description: ""
trigger:
- platform: state
entity_id: sensor.power_grid_usage
condition:
- condition: state
entity_id: binary_sensor.charger
state: "on"
- condition: state
entity_id: device_tracker.location_tracker
state: home
- condition: state
entity_id: input_boolean.smart_charge
state: "on"
action:
- variables:
grid_usage: "{{ states('sensor.power_grid_usage') | float }}"
max_charging_amps: 16
min_charging_amps: 1
grid_voltage: 240
- choose:
- conditions:
- condition: template
value_template: "{{ grid_usage > 0 }}"
sequence:
- service: switch.turn_on
target:
entity_id: switch.charger
data: {}
- service: number.set_value
target:
entity_id: number.charging_amps
data:
value: >
{% set charger_power_draw = (states('number.charging_amps') |
int) * grid_voltage %} {% set charging_amps = ((grid_usage +
charger_power_draw) / grid_voltage) | round(0, 'floor') | int %}
{{ [max_charging_amps, charging_amps] | min }}
- conditions:
- condition: template
value_template: "{{ grid_usage <= 0 }}"
sequence:
- service: number.set_value
target:
entity_id: number.charging_amps
data:
value: >
{% set deficit_watts = -1 * grid_usage %} {% set
current_charging_amps = states('number.charging_amps') | int %}
{% set required_charging_amps = ((deficit_watts / grid_voltage)
| round(0, 'ceil')) | int %} {% set updated_charging_amps =
current_charging_amps - required_charging_amps %} {% if
updated_charging_amps < min_charging_amps %}
1
{% else %}
{{ [max_charging_amps, updated_charging_amps] | min }}
{% endif %}
- choose:
- conditions:
- condition: template
value_template: "{{ states('number.charging_amps') | int <= 1 }}"
sequence:
- service: switch.turn_off
target:
entity_id: switch.charger
data: {}
mode: single
As you can see, I’ve added some things in the process. Here’s what’s going on:
- The conditions for initiating the automation are that the car charge connector needs to be connected, the car needs to be at my home location and a boolean called ‘smart charge’ needs to be on. The latter comes in handy for switching between smart charging (using this automation) and regular (max power) charging, as you will see later on.
- I’ve set variables for the grid usage, min and max amps and the grid voltage
- I’ve separated the actions for when grid voltage > 0 and voltage <= 0, as they have different workings
- each time the grid usage changes, it triggers a new calculation. The <= 0 was the nifty part and took me some time, as I first completely overlooked the fact that when the grid voltage updated, I needed to store the previous values to calculate the deficit and set the amps accordingly.
Now that this worked, I decided to add some use cases.
Here’s the automation that starts regular (full power) charging when the smart charging boolean is turned off manually (or by automation):
alias: "[Tesla] Max power charge when smart charging is switched off"
description: ""
trigger:
- platform: state
entity_id: input_boolean.smart_charge
to: "off"
condition:
- condition: state
entity_id: binary_sensor.charger
state: "on"
- condition: template
value_template: "{{ states('sensor.battery')|float < states('number.charge_limit')|float }}"
action:
- service: switch.turn_on
target:
entity_id: switch.charger
data: {}
- service: number.set_value
target:
entity_id: number.charging_amps
data:
value: |
{{ states.number.charging_amps.attributes.max | int }}
As a sanity measure (to avoid the situation that I connect the car and it doesn’t start charging at all), I decided to create an automation that resets the smart charging boolean (so next time it connects, it starts charging regularly on full power), everytime the charge connector disconnects from the charge port:
alias: "[Tesla] Reset smart charging state upon charge connector disconnect"
description: ""
trigger:
- platform: state
entity_id: binary_sensor.charger
to: "off"
action:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.smart_charge
data: {}
- service: number.set_value
target:
entity_id: number.charging_amps
data:
value: 16
Here’s the automation that asks me if I want to start smart charging, when I connect the car during daylight:
alias: "[Tesla] Ask user for smart charging when charge port connector connects"
description: ""
trigger:
- platform: state
entity_id: binary_sensor.charger
to: "on"
condition:
- condition: state
entity_id: device_tracker.location_tracker
state: home
- condition: state
entity_id: sun.sun
state: above_horizon
action:
- service: notify.user
data:
message: Enable smart charging?
data:
actions:
- action: enable_smart_charging
title: Enable
- action: disable_smart_charging
title: Disable
And here’s how the actions are handled:
alias: "[Tesla] smart charging iOS actions"
description: ""
trigger:
- platform: event
event_type: ios.notification_action_fired
event_data:
actionName: enable_smart_charging
- platform: event
event_type: ios.notification_action_fired
event_data:
actionName: disable_smart_charging
action:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.event.data.actionName == 'enable_smart_charging' }}"
sequence:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.smart_charge
data: {}
- conditions:
- condition: template
value_template: "{{ trigger.event.data.actionName == 'disable_smart_charging' }}"
sequence:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.smart_charge
data: {}
- service: switch.turn_on
target:
entity_id: switch.charger
data: {}
- service: number.set_value
target:
entity_id: number.charging_amps
data:
value: 16
Next, I wanted to receive a notification when the car is smart charging, but there is no surplus power production for a prolonged period of time (30 minutes), and I wanted to be able to switch from smart charging to regular charging:
alias: "[Tesla] iOS notify upon prolonged solar power deficit"
description: ""
trigger:
- platform: state
entity_id: switch.charger
to: "off"
for:
minutes: 30
condition:
- condition: state
entity_id: input_boolean.smart_charge
state: "on"
- condition: template
value_template: "{{ states('sensor.battery')|float < states('number.charge_limit')|float }}"
- condition: state
entity_id: binary_sensor.charger
state: "on"
action:
- service: notify.user
data:
message: No solar power available, disable smart charging?
data:
actions:
- action: TURN_OFF_SMART_CHARGING
title: Disable
Here’s how the actions are handled:
alias: "[Tesla] iOS actions prolonged solar power deficit"
description: ""
trigger:
- platform: event
event_type: ios.notification_action_fired
event_data:
actionName: TURN_OFF_SMART_CHARGING
condition: []
action:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.smart_charge
data: {}
And for the opposite situation - notify me when there is a power surplus, but the car isn’t charging at all or is regular charging, so I can switch to smart charging:
alias: "[Tesla] iOS notify upon solar power production surplus"
description: ""
trigger:
- platform: numeric_state
entity_id: sensor.power_grid_usage
above: 500
for:
minutes: 5
condition:
- condition: state
entity_id: input_boolean.smart_charge
state: "off"
- condition: template
value_template: "{{ states('sensor.battery')|float < states('number.charge_limit')|float }}"
- condition: state
entity_id: binary_sensor.charger
state: "on"
- condition: sun
before: sunset
after: sunrise
action:
- service: notify.user
data:
message: Excessive solar power production, enable smart charging?
data:
actions:
- action: TURN_ON_SMART_CHARGING
title: Inschakelen
And for the handling of the actions:
alias: "[Tesla] iOS actions solar power production surplus"
description: ""
trigger:
- platform: event
event_type: ios.notification_action_fired
event_data:
actionName: TURN_ON_SMART_CHARGING
condition: []
action:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.smart_charge
data: {}
Well, that’s about it. I’ve been using this for about a month now, and it seems to cover almost all of my use cases. Let me know what you think!