How I Optimized Charging My EV Using Excess Solar Power with Home Assistant
TL;DR: How I transformed my basic EV charger into a smart one for just $6, enabling it to charge from excess solar and battery storage—skipping the need for a $600-$1200 aftermarket charger with fewer features and no battery support.
Overview
As a Tesla owner with an EG4 home solar setup, I wanted to optimize my charging process to make the most out of excess solar energy. I charge my vehicle with the mobile connector that came with my car. It lacks internet connectivity, cannot be programmed to start or stop charging remotely, and cannot dynamically adjust amperage based on the excess energy currently being harvested.
This inspired me to explore automation options for my EV charging, ultimately leading to the development of two automations in Home Assistant tailored to achieve my goals.
While I thoroughly enjoyed going down this rabbit hole, I’d not recommend it to others unless you really enjoy geeking out. Premium vertical solar integrations from Tesla, Span, Enphase, and aftermarket EV smart chargers like Emporia and Wallbox offer reliable and robust smart EV charging solutions compared to the custom setup I’m about to describe in detail. They're worth considering! While they do have some limitations, they provide a hassle-free experience. That said, I wanted to challenge myself to build a custom setup on the cheap and add some functionality they don’t support.
Table of Contents
Background
When I installed solar panels and batteries, I initially explored upgrading to a smart EV charger, such as the Emporia Level 2 EV Charger with PowerSmart Load Management or the Wallbox Pulsar Plus paired with the required Power Meter.
Both of these solutions can adjust the amperage they supply to an EV based on excess solar production. By dynamically reducing or increasing the charging rate, they ensure efficient use of available solar energy, minimize reliance on the grid, and help avoid overloading the system during periods of lower solar output.
However, their costs range from $600 to $1,200. Moreover, the Emporia charger depends on a cloud provider that could potentially shut down, leaving the unit unusable. Neither option offered integrations that consider home batteries, limiting sourcing options to grid-only, grid and photovoltaic (PV), or PV alone.
For me, it was important to account for both charging and discharging my home battery when charging my electric vehicle (EV). While I’m comfortable using some of my home battery’s stored energy to charge my EV, it's not efficient and I want to ensure there’s always enough reserve for essential household needs and emergencies.
While researching how to integrate my EV charger into my solar setup, I discovered that my inverter features a Smart Loads plug.
Smart Loads are loads that the user wants to intelligently, strategically, and automatically enable and disable for the purposes of Load Shedding and Power Shedding in order to maximize Time of Use savings, Off-Grid operation, or maximize or minimize power sold back to the grid.
Initially, it seemed like a promising solution, offering configurable thresholds and failover scenarios to manage how the grid, batteries, and PV supply power to the plug.
However, further research revealed Smart Loads to be buggy, with unclear logic and poor documentation. My conclusion was that the inverter’s hardware was originally designed as an input plug for generators, and later adapted via software for dual-purpose use as a power output—without fully addressing the hardware requirements for the additional functions that were added in software. I decided I wasn't comfortable relying on a hacky plug for my EV charging.
To address these challenges, I aimed to purpose build a solution using a combination of software tools and low-cost hardware components. The setup described below dynamically adjusts the Tesla’s charging amperage based on real-time solar production, home battery status, and my specific charging requirements.
I chose to use the ESPHome Tesla BLE integration, which communicates with the car via Bluetooth to adjust charging settings, instead of the Tesla Fleet API for several reasons. By avoiding the Fleet API, I sidestepped potential rate limits and dependency on a hosted cloud service for real-time adjustments. I also anticipated Tesla introducing fees that would make the API prohibitively expensive, a prediction that has since proven accurate.
In a previous post, I explained how I set up ESPHome on an ESP32 chip and integrated it with my Tesla and Home Assistant to optimize solar charging.
In this post, I’ll guide you through automating the system using Home Assistant, ESPHome, Teslamate, Eclipse MQTT, and a EG4 solar inverter. I’ll also show you how to use a dashboard to monitor the automations and adjust parameters.
A quick note: if you decide to use this, proceed at your own risk—I make no guarantees about the reliability of my Rube Goldberg-style charging system.
Use Case Overview
The goal is to dynamically adjust the Tesla's charging amperage based on:
- Excess Solar Production: Utilize surplus solar energy that would otherwise be stored in home batteries or exported to the grid.
- Home Battery Status: Prioritize charging the home battery based on preferred battery reserves.
- Car's Rated Battery Range: Adjust charging priority dynamically for daily use—lowering priority when the range exceeds 120 miles and raising it when the range drops below 99 miles.
- Support for Manual Override: For long trips requiring a full charge, I need the option to activate a highly reliable “one-click” mode to charge the car at maximum amperage using the grid while setting a custom battery discharge threshold to prevent the home battery from depleting to its minimum allowed levels. Although this scenario is infrequent, it is crucial that, once activated, it runs uninterrupted by any other automations during the charging period.
Energy Flow and Efficiency Challenges
I quickly discovered that optimizing energy usage requires a basic understanding of the physics of electrical flow, particularly the interplay between DC (direct current) and AC (alternating current). Solar panels and home batteries typically operate on DC power, while most household appliances use AC. Converting between these forms introduces efficiency losses, and while my solar system can invert in one direction and convert in the other, it cannot perform both functions simultaneously.
In an ideal setup, I would have the flexibility to power my house with excess PV energy during the day while simultaneously charging my home battery and, when needed, discharging the battery to supply power to both my car and home concurrently. Unfortunately, I do not have that option.
To maximize energy efficiency and operate within physical constraints, I discovered that I need to carefully manage power flows, balancing the charging of the home and car batteries during the day with excess solar energy while reserving the battery to power the house at night.
A smart energy management system is essential to address these challenges. It optimizes energy efficiency, fulfills typical daily EV driving requirements, and ensures the home battery remains a dependable power source, reducing reliance on the grid. To achieve this, I developed a few automations.
About the Automations
- Charge Tesla from PV Excess (Automatic):
- Purpose: Automatically harvest excess solar energy to charge the Tesla, suitable for daily driving needs.
- Operation: This automation runs during daylight hours when solar production is sufficient, dynamically adjusting the EV charging rate based on real-time solar data, the car’s battery range, and home battery thresholds.
- Priority: Lower priority; designed not to interfere with manual charging needs.
- Charge Tesla Without Draining Home Battery (Manual):
- Purpose: Manually triggered to charge the Tesla at maximum power without fully depleting the home battery. Designed for infrequent usage when the car needs to be fully charged for a long road trip.
- Operation: Temporarily overrides the PV excess automation to prioritize rapid charging, with an adjustable threshold for the battery’s discharge rate. This threshold ensures the home battery contributes without being fully drained and resets to its original setting afterward.
- Priority: Higher priority; it can override the PV excess automation but not vice versa, preventing potential disruptions to important travel plans.
By implementing both automations, it's possible to:
- Optimize Daily Charging: Make the most of solar energy for everyday driving.
- Ensure Readiness for Trips: Quickly charge the car when needed without worrying about home battery levels.
Prerequisites
Brace yourself Jason. To set up this system, you'll need the following:
- Home Assistant instance: To host the automations, integrate all the data sources and expose a real-time dashboard.
- Monitor My Solar HACS Integration with their Dongle: For reading and writing data to an EG4 18kpv solar inverter connected to the grid, batteries, PV panels and an EV charger.
- Teslamate with the Teslamate HA MQTT Integration: For reading the vehicles rated battery range in miles.
- Eclipse MQTT (in bridge mode): To allow Home Assistant to import MQTT data from both the solar inverter Dongle and vehicle stats from Teslamate.
- ESPHome Tesla BLE with an ESP32 Chip: To communicate with the Tesla over Bluetooth to know when the charge flap is open, adjust amperage rates, and manage charging.
MQTT Broker Configuration
Since Home Assistant only allows one MQTT integration, I needed to bridge data from both the solar inverter dongle and Teslamate. I used Eclipse Mosquitto to handle this.
Update your Mosquitto configuration file at /usr/local/etc/mosquitto/mosquitto.conf
:
# Default listener for authenticated clients (dongle)
listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd
# Listener for TeslaMate (no authentication required)
listener 1884 0.0.0.0
allow_anonymous true
# Persistence settings
persistence true
persistence_location /usr/local/var/lib/mosquitto/
# Logging settings
log_dest file /usr/local/var/log/mosquitto/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information
# Bridge configuration to ingest TeslaMate data
connection teslamate_bridge
address tesla.yourdomain.com:1883
topic teslamate/# in
Note: Replace tesla.yourdomain.com
with your actual Teslamate domain.
Key Details:
- Multiple Listeners: Configured two listeners to handle authenticated (dongle) and unauthenticated (Teslamate) clients.
- Bridging: The
connection
andtopic
settings bridge data from Teslamate into the MQTT broker. - Note: My Teslamate instance is running on my Tailscale network with no open ports, so I’m not using a password. However, you might want to consider setting one!
Home Assistant Configuration
All these YAML configuration files must be added to Home Assistant, and the system needs to be restarted for the changes to take effect.
Add to configuration.yaml
To keep things organized, I included separate YAML files for different configurations. These files will need to be created and live at the same level as your Home Assistant configuration.yaml
.
input_number: !include tesla_charging.yaml
input_datetime: !include input_datetime.yaml
input_boolean: !include input_boolean.yaml
Creating Input Numbers for Thresholds
Create tesla_charging.yaml
with the following content:
tesla_range_threshold:
name: Tesla Range Threshold
min: 50
max: 200
step: 1
unit_of_measurement: "mi"
icon: mdi:car-battery
mode: box
initial: 120
tesla_priority_range_threshold:
name: Tesla Priority Range Threshold
min: 50
max: 200
step: 1
unit_of_measurement: "mi"
icon: mdi:car-electric
mode: box
initial: 99
tesla_high_battery_threshold:
name: Home Battery High Threshold
min: 0
max: 100
step: 1
unit_of_measurement: "%"
icon: mdi:battery-high
mode: box
initial: 65
tesla_low_battery_threshold:
name: Home Battery Low Threshold
min: 0
max: 100
step: 1
unit_of_measurement: "%"
icon: mdi:battery-low
mode: box
initial: 50
tesla_home_power_buffer:
name: Home Power Buffer
min: 0
max: 2000
step: 100
unit_of_measurement: "W"
icon: mdi:home-lightning-bolt
mode: box
initial: 500
tesla_validation_period:
name: Validation Wait Time
min: 1
max: 60
step: 1
unit_of_measurement: minutes
icon: mdi:timer-outline
mode: box
initial: 3 # Default wait time
Key Details:
- Thresholds: Define customizable thresholds for car range, battery levels, and power buffer.
- Adjustable Parameters: These inputs allow you to tweak the system behavior directly from a friendly dashboard in Home Assistant so you don't need to dive into code to modify the Charge Tesla from PV Excess automation business logic.
Input Datetime for Tracking
Used to store timestamps for when certain conditions are met.
Create input_datetime.yaml
:
tesla_last_below_min_amps:
name: Last Time Below Min Amps
has_date: true
has_time: true
Binary Sensors
Define sensors to monitor the charging status of the car and home battery.
Add to binary_sensor.yaml
:
- platform: template
sensors:
tesla_charging_status:
friendly_name: "EV Charging"
value_template: >-
{{ is_state('switch.tesla_ble_your_device_id_charger_switch', 'on') }}
attribute_templates:
power: >-
{% if is_state('switch.tesla_ble_your_device_id_charger_switch', 'on') %}
{% set amps = states('number.tesla_ble_your_device_id_charging_amps') | float %}
{{ (amps * 240) | round }}
{% else %}
0
{% endif %}
home_battery_charging_status:
friendly_name: "Battery Charging"
value_template: >-
{% set flow = states('sensor.dongle_device_id_batteryflow_live') | float %}
{{ flow > 0 }}
attribute_templates:
power: >-
{% set flow = states('sensor.dongle_device_id_batteryflow_live') | float %}
{{ flow if flow > 0 else 0 }}
Note: To customize code on this page for your own use, you'll need to replace:
your_device_id
with your Tesla BLE device ID (e.g., if your device ID is abc123, usetesla_ble_abc123_charging_amps
)dongle_device_id
with your device ID (e.g., if your dongle ID is dongle-XX:XX:XX:XX:XX:XX, usedongle_XX_XX_XX_XX_XX_XX_soc
)
Input Boolean for Manual Charging
To manually control the charging process and prevent automations from interfering.
Create input_boolean.yaml
:
tesla_manual_charging_active:
name: Tesla Manual Charging Active
initial: off
The Automation Workflows
In Home Assistant, the following two automations run independently: one is manual, and the other is automatic. They are specifically designed to avoid interfering with each other, even if triggered simultaneously. Additionally, they include some mechanisms to recover and resume functionality if Home Assistant is interrupted or restarted.
Charge Tesla Without Draining Home Battery
This automation is triggered manually and is designed to ensure that charging the Tesla doesn't deplete the home battery below desired thresholds. It also includes recovery logic to reset settings if charging is interrupted. Currently, I’m allowing my batteries to help charge the car when I’m in a rush, though this may not be a long-term solution due to efficiency losses—around 3% for DC-to-DC conversion and another 10% for DC-to-AC. My reasoning for doing this now is that I installed the system in winter and won’t start earning credits by selling energy back until summer.
Business Logic
- Manual Activation: This automation is manually triggered when you need to charge the car at maximum power, such as before a long trip.
- Home Battery Protection:
My home batteries are set to discharge to 10% when connected to the grid.- If the home battery's state of charge (SOC) is above 51%, it allows the battery to discharge down to 50%.
- If the SOC is below 50%, it sets the end of discharge (EOD) to 50% to prevent the battery from draining further. When EV charging is complete the EOD is set back to 10%.
- Note: I’ve heard that the chips in the EG4 inverter use NOR flash, which may have a write cycle lifespan of 10,000 to 100,000 cycles. To preserve their longevity, I’ve been cautious about minimizing the frequency of register writes.
- Charging Process:
- Wakes up the car and sets the charging amperage to 32A (maximum).
- Turns on the charger switch to start charging.
- Monitoring and Adjustment:
- Checks if the charger is still on and adjusts the EOD based on the home battery SOC.
- Stops when the car reaches its charging limit or if charging is manually stopped.
- Recovery Logic:
- If charging is interrupted, the automation resets the home battery EOD to its initial value.
- Turns off the manual charging indicator to allow other automations to run when complete even if there's a reboot mid process.
Adjustable Parameters
- Home Battery Thresholds:
- Adjust
tesla_high_battery_threshold
andtesla_low_battery_threshold
to set when the automation should adjust the home battery EOD.
- Adjust
- Car Charging Limit:
- Set via the car's settings; the automation respects the car's charging limit (e.g. 80%, etc.)
Automation YAML:
alias: Charge Tesla Don't Kill Home Battery
description: >
Charges Tesla while managing home battery discharge levels with recovery logic.
Ensures the home battery doesn't drain excessively during car charging.
trigger:
- platform: event
event_type: call_service
event_data:
domain: automation
service: trigger
service_data:
entity_id: automation.charge_tesla_dont_kill_home_battery
- platform: homeassistant
event: start
- platform: state
entity_id: switch.tesla_ble_your_device_id_charger_switch
to: "off"
condition:
- condition: state
entity_id: input_boolean.tesla_manual_charging_active
state: "on"
- condition: template
value_template: >
{{ (states('number.tesla_ble_your_device_id_battery_level') | float) <
(states('number.tesla_ble_your_device_id_charging_limit') | float) }}
action:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.tesla_manual_charging_active
- service: switch.turn_on
target:
entity_id: switch.tesla_ble_your_device_id_ble_connection
- delay: "00:00:01"
- service: button.press
target:
entity_id: button.tesla_ble_your_device_id_wake_up
- delay: "00:00:02"
- service: number.set_value
target:
entity_id: number.tesla_ble_your_device_id_charging_amps
data:
value: 32
- variables:
initial_eod: "{{ states('number.dongle_device_id_eod') }}"
- choose:
- conditions:
- condition: numeric_state
entity_id: sensor.dongle_device_id_soc
above: 51
sequence:
- service: number.set_value
target:
entity_id: number.dongle_device_id_eod
data:
value: 10
- conditions:
- condition: numeric_state
entity_id: sensor.dongle_device_id_soc
below: 50
sequence:
- service: number.set_value
target:
entity_id: number.dongle_device_id_eod
data:
value: 50
- service: switch.turn_on
target:
entity_id: switch.tesla_ble_your_device_id_charger_switch
- repeat:
sequence:
- condition: state
entity_id: switch.tesla_ble_your_device_id_charger_switch
state: "on"
- choose:
- conditions:
- condition: numeric_state
entity_id: sensor.dongle_device_id_soc
below: 50
sequence:
- service: number.set_value
target:
entity_id: number.dongle_device_id_eod
data:
value: 50
- delay: "00:00:30"
- condition: template
value_template: >
{{ (states('number.tesla_ble_your_device_id_battery_level') | float) <
(states('number.tesla_ble_your_device_id_charging_limit') | float) }}
until:
- condition: or
conditions:
- condition: template
value_template: >
{{ (states('number.tesla_ble_your_device_id_battery_level') | float) >=
(states('number.tesla_ble_your_device_id_charging_limit') | float) }}
- condition: state
entity_id: switch.tesla_ble_your_device_id_charger_switch
state: "off"
- service: input_boolean.turn_off
target:
entity_id: input_boolean.tesla_manual_charging_active
- service: number.set_value
target:
entity_id: number.dongle_device_id_eod
data:
value: "{{ initial_eod }}"
Note: Replace your_device_id
with the name of your ESPHome device and dongle_device_id
with the name of your dongle device macID.
Key Modifications and Choices:
- Manual Charging Indicator: Uses
input_boolean.tesla_manual_charging_active
to prevent other automations from interfering. - Recovery Logic: If charging is interrupted (e.g., charger switch turns off), the automation resets the home battery's EOD to its initial value.
- Dynamic EOD Adjustment: Adjusts the home battery EOD based on its current SOC.
- Repeat Loop with Exit Conditions: Continues to adjust EOD and monitor charging until the car is fully charged or charging stops.
Charge Tesla from PV Excess
This automation is triggered automatically and is designed to dynamically adjust the Tesla's charging amperage based on excess solar production and the state of the home battery.
Business Logic
- Automatic Operation: Runs automatically during daylight hours when the car is plugged in and excess solar power is available.
- Charging Logic:
- Car Range Priority:
- If car range is below the priority threshold (e.g., 99 miles), prioritize charging the car with up to 95% of the excess solar power.
- If car range is between the priority threshold and the general threshold (e.g., 99–120 miles), allocate 50–70% of excess power to the car based on home battery SOC.
- If car range is above the general threshold (e.g., above 120 miles), allocate less power to the car, prioritizing the home battery.
- Home Battery SOC:
- Adjusts the allocation of excess power based on the home battery's SOC.
- If the home battery is low, more power is allocated to it; if it's high, more power can go to the car.
- Car Range Priority:
- Amperage Calculation:
- Calculates the available amperage for charging based on excess solar power.
- Applies a safety limit to ensure the amperage stays between 0 and 32A.
- Validation Period:
- Includes a validation period (e.g., 3 minutes) to ensure that once the amperage falls to zero it is sustainable before attempting re-engage the charger.
- This is one of the few stopgaps to prevent frequent on/off cycles that could lead to vampire drain.
Adjustable Parameters
- Thresholds:
tesla_range_threshold
: Above this range, car charging is deprioritized.tesla_priority_range_threshold
: Below this range, car charging is prioritized.tesla_high_battery_threshold
andtesla_low_battery_threshold
: Define the SOC levels for the home battery to adjust power allocation.
- Home Power Buffer:
tesla_home_power_buffer
: Watts reserved to account for rapid fluctuations in home consumption.
- Validation Period:
tesla_validation_period
: Time in minutes that the amperage must be above the minimum sustainable level (e.g. zero) before starting charging.
Automation YAML:
alias: 🔌 Charge Tesla From PV Excess
description: >
Optimizes Tesla charging based on solar production and home battery state.
Includes a validation period to prevent frequent adjustments.
trigger:
- platform: state
entity_id: binary_sensor.tesla_plugged_in
to: "on"
- platform: time_pattern
minutes: "/1"
condition:
- condition: state
entity_id: input_boolean.tesla_manual_charging_active
state: "off"
- condition: state
entity_id: binary_sensor.tesla_plugged_in
state: "on"
- condition: template
value_template: >
{{ states('number.tesla_ble_your_device_id_battery_level') | float <
states('number.tesla_ble_your_device_id_charging_limit') | float }}
- condition: numeric_state
entity_id: sensor.dongle_device_id_pall
above: 1000
- condition: sun
before: sunset
after: sunrise
- condition: template
value_template: >
{% set solar_production = states('sensor.dongle_device_id_pall') | float %}
{% set home_consumption = states('sensor.dongle_device_id_pload') | float %}
{% set home_buffer_watts = states('input_number.tesla_home_power_buffer') | float %}
{% set solar_excess = solar_production - home_buffer_watts - home_consumption %}
{{ solar_excess > 0 }}
action:
- variables:
calculated_amps: >
{# Retrieve thresholds #}
{% set range_threshold = states('input_number.tesla_range_threshold') | float %}
{% set priority_range_threshold = states('input_number.tesla_priority_range_threshold') | float %}
{% set high_battery_threshold = states('input_number.tesla_high_battery_threshold') | float %}
{% set low_battery_threshold = states('input_number.tesla_low_battery_threshold') | float %}
{% set home_buffer_watts = states('input_number.tesla_home_power_buffer') | float %}
{# Calculate available power #}
{% set solar_production = states('sensor.dongle_device_id_pall') | float %}
{% set home_consumption = states('sensor.dongle_device_id_pload') | float %}
{% set solar_excess = solar_production - home_buffer_watts - home_consumption %}
{% set excess_pv_energy = solar_excess / 240 %}
{% set car_range = states('sensor.tesla_rated_battery_range_mi') | float %}
{% set house_battery_soc = states('sensor.dongle_device_id_soc') | float %}
{# Charging logic #}
{% if car_range < range_threshold %}
{# Below range threshold #}
{% if car_range < priority_range_threshold %}
{% set max_amps = (excess_pv_energy * 0.95) | int %}
{% elif house_battery_soc > high_battery_threshold %}
{% set max_amps = (excess_pv_energy * 0.70) | int %}
{% else %}
{% set max_amps = (excess_pv_energy * 0.50) | int %}
{% endif %}
{% else %}
{# Above range threshold #}
{% if house_battery_soc < low_battery_threshold %}
{% set max_amps = (excess_pv_energy * 0.30) | int %}
{% elif house_battery_soc < high_battery_threshold %}
{% set max_amps = (excess_pv_energy * 0.40) | int %}
{% else %}
{% set max_amps = (excess_pv_energy * 0.60) | int %}
{% endif %}
{% endif %}
{# Safety limits #}
{% if max_amps > 32 %}32{% elif max_amps < 0 %}0{% else %}{{ max_amps }}{% endif %}
- condition: template
value_template: >
{% set current_time = now() %}
{% set last_below_min = states('input_datetime.tesla_last_below_min_amps') %}
{% set validation_period_minutes = states('input_number.tesla_validation_period') | float %}
{% set has_sufficient_amps = calculated_amps | int >= min_sustainable_amps %}
{% if has_sufficient_amps %}
{% if not last_below_min %}
true
{% else %}
{% set time_diff = (as_timestamp(current_time) - as_timestamp(last_below_min)) / 60 %}
{{ time_diff >= validation_period_minutes }}
{% endif %}
{% else %}
false
{% endif %}
- choose:
- conditions:
- condition: template
value_template: "{{ calculated_amps | int < min_sustainable_amps }}"
sequence:
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.tesla_last_below_min_amps
data:
timestamp: "{{ now().timestamp() }}"
- choose:
- conditions:
- condition: template
value_template: "{{ calculated_amps | int >= min_sustainable_amps }}"
sequence:
- service: switch.turn_on
target:
entity_id: switch.tesla_ble_your_device_id_ble_connection
- delay: "00:00:01"
- service: button.press
target:
entity_id: button.tesla_ble_your_device_id_wake_up
- delay: "00:00:02"
- service: number.set_value
target:
entity_id: number.tesla_ble_your_device_id_charging_amps
data:
value: "{{ calculated_amps }}"
- service: switch.turn_on
target:
entity_id: switch.tesla_ble_your_device_id_charger_switch
- conditions:
- condition: template
value_template: "{{ calculated_amps | int < min_sustainable_amps }}"
- condition: state
entity_id: switch.tesla_ble_your_device_id_charger_switch
state: "on"
sequence:
- service: switch.turn_off
target:
entity_id: switch.tesla_ble_your_device_id_charger_switch
variables:
min_sustainable_amps: 1
mode: single
max_exceeded: silent
Note: Replace your_device_id
with the name of your ESPHome device and dongle_device_id
with the name of your dongle device macID.
Key Features:
- Dynamic Amperage Calculation: Adjusts charging amperage based on solar excess, car range, and battery SOC.
- Validation Period: Includes a validation period (adjustable via the dashboard) to ensure the charging current remains above a minimum sustainable level before re-engaging the charger.
- Priority Logic: Prioritizes car charging or home battery charging based on defined thresholds.
- Safety Checks: Ensures amperage stays within safe limits (1–32 A).
Key Modifications and Choices:
- Avoiding Frequent BLE Communication: The validation period attempts to reduces the frequency of Bluetooth communications with the car, which can be resource-intensive.
- Manual Charging Override: Checks if manual charging is active to prevent automation conflicts.
- Use of Variables: Improves readability and maintainability by using variables for thresholds and calculations.
Custom Dashboard Setup
I created a custom dashboard in Home Assistant to monitor and adjust the system parameters.
Dashboard YAML Configuration:
title: Tesla PV Charging
views:
- title: Main
path: main
badges: []
cards:
- type: markdown
style: |
ha-card {
height: auto;
min-height: 100px;
padding: 16px;
margin: 8px;
background: var(--card-background-color);
opacity: 0;
animation: fadeIn 0.5s ease-in forwards;
animation-delay: 0.5s;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
ha-markdown {
font-size: 14px !important;
line-height: 1.8 !important;
min-height: 400px;
}
content: |-
{% if states('sensor.tesla_rated_battery_range_mi') != 'unavailable'
and states('sensor.dongle_device_id_soc') != 'unavailable' %}
{% set range_threshold = states('input_number.tesla_range_threshold') | float(120) %}
{% set priority_range_threshold = states('input_number.tesla_priority_range_threshold') | float(100) %}
{% set high_battery_threshold = states('input_number.tesla_high_battery_threshold') | float(80) %}
{% set low_battery_threshold = states('input_number.tesla_low_battery_threshold') | float(20) %}
{% set solar_production = states('sensor.dongle_device_id_pall') | float(0) %}
{% set home_consumption = states('sensor.dongle_device_id_pload') | float(0) %}
{% set home_buffer_watts = states('input_number.tesla_home_power_buffer') | float(500) %}
{% set solar_excess = solar_production - home_buffer_watts - home_consumption %}
{% set excess_pv_energy = solar_excess / 240 %}
{% set car_range = states('sensor.tesla_rated_battery_range_mi') | float(0) %}
{% set house_battery_soc = states('sensor.dongle_device_id_soc') | float(0) %}
{% set actual_charging_current = states('sensor.tesla_charger_actual_current') | float(0) %}
đźš™ Range: {{ car_range | round(1) }} mi
🔋 Added: {{ states('sensor.tesla_charge_energy_added') | float(0) | round(1) }} kWh{% if car_range < priority_range_threshold %}
⚠️ Car: Priority Charging needed (below {{ priority_range_threshold }}mi){%- elif car_range < range_threshold -%}
đź“Š Car: Normal Charging mode (below {{ range_threshold }}mi){% else %}
â›˝ Car: Conservative Charging (above {{ range_threshold }}mi){% endif %}
đźš— Set Amps: {{ states('number.tesla_ble_your_device_id_charging_amps') | float(0) }}A{% set manual_charging = is_state('input_boolean.tesla_manual_charging_active', 'on') %}
⚡ Manual Grid Charging: {{ "On" if manual_charging else "Off" }}
đź‘€ EV Charging: {% if actual_charging_current > 0 %}Yes {{ (actual_charging_current * 240) | round }}W{% else %}No{% endif %}
🏡 Battery: {{ house_battery_soc | round(1) }}%
🪫 Discharge Level: {{ states('number.dongle_device_id_eod') | float(10) }}%{% if house_battery_soc < low_battery_threshold %}
⚠️ House: Prioritizing Home Battery (below {{ low_battery_threshold }}%){% elif house_battery_soc < high_battery_threshold %}
đź“Š House: Moderate Charging mode (below {{ high_battery_threshold }}%){% else %}
âś… House: Increased Car Charging enabled (above {{ high_battery_threshold }}%){% endif %}
đź‘€ Battery Charging: {% if is_state('binary_sensor.home_battery_charging_status', 'on') %}Yes {{ state_attr('binary_sensor.home_battery_charging_status', 'power') | float(0) | round }}W{% else %}No{% endif %}
🌞 Harvest: {{ solar_production | round }}W
🏠Use: {{ home_consumption | round }}W
🛟 Buffer: {{ home_buffer_watts | round }}W
âž• Excess: {{ solar_excess | round }}W
🔌 Available for car: {{ excess_pv_energy | round(1) }}A
{% else %}
Loading data...
{% endif %}
- type: entities
title: System Thresholds
entities:
- entity: input_number.tesla_range_threshold
name: Range Threshold
- entity: input_number.tesla_priority_range_threshold
name: Priority Range
- entity: input_number.tesla_high_battery_threshold
name: High Battery
- entity: input_number.tesla_low_battery_threshold
name: Low Battery
- entity: input_number.tesla_home_power_buffer
name: Home Power Buffer
- entity: input_number.tesla_validation_period
name: Wait Time >= 1A
control_mode: box
- type: entities
entities:
- type: button
name: 🔌 Tesla 🚙 Don't Kill 🏡 🔋
icon: mdi:car-electric-outline
tap_action:
action: call-service
service: automation.trigger
target:
entity_id: automation.charge_tesla_dont_kill_home_battery
- type: horizontal-stack
cards:
- type: gauge
name: Car Range
entity: sensor.tesla_rated_battery_range_mi
min: 0
max: 300
severity:
green: 120
yellow: 99
red: 0
- type: gauge
name: Home Battery
entity: sensor.dongle_device_id_soc
unit: '%'
min: 0
max: 100
severity:
green: 65
yellow: 50
red: 0
- type: history-graph
title: Power Distribution (24h)
hours_to_show: 24
entities:
- entity: sensor.dongle_device_id_pall
name: Solar Production
- entity: sensor.dongle_device_id_pload
name: Home Consumption
- entity: number.tesla_ble_your_device_id_charging_amps
name: Car Charging Rate
Key Features:
- Dynamic Data Display: The markdown card shows real-time data and status messages.
- Adjustable Thresholds: Entities card allows you to adjust system thresholds on the fly.
- Manual Control: A button to manually trigger the "Charge Tesla Without Draining Home Battery" automation.
- Visual Gauges: Quick view of car range and home battery status.
- Historical Graph: Monitor power distribution over the last 24 hours.
Note: Replace your_device_id
and dongle_device_id
with your actual device IDs in the dashboard configuration.
Conclusion
By integrating data into Home Assistant via MQTT from Teslamate, my solar inverter using the Monitor My Solar dongle, and my Tesla using an ESP32 chip running ESPHome Tesla BLE, I created a dynamic system that optimizes Tesla charging based on real-time solar production and home energy demands.
The customizable dashboard provides monitoring and adjustment of parameters, ensuring efficient management of both the car and home battery. Additionally, when rapid EV charging is needed, the system avoids depleting the home battery.
Although I learned a lot and already had many prerequisites in place, which made the configuration relatively quick, I find the setup fragile and likely to be annoying to maintain. I plan to keep it running and hope it won’t cause too many headaches. However, I intend to be cautious about investing significant additional effort into optimizing it unless the improvements are clearly worthwhile.
Benefits of this setup:
- Energy Efficiency: Maximizes the use of excess solar energy.
- Cost Savings: Reduces reliance on grid electricity, lowering energy bills.
- Environmental Impact: Enhances the use of renewable energy sources.
- Affordability: Extremely budget-friendly, with the ESP32 chip costing $6.
- Customization and Control: Allows you to control the logic and easily tweak the setup to meet specific needs.
I’d love to hear your thoughts, feedback, or experiences if you actually try using this approach. Hopefully, we’ll see simpler aftermarket solutions from vehicle manufacturers integrating this functionality into their chargers in an open and accessible way.
Happy charging!