Introduction
I’d like to share my Home Assistant project for charging my electric vehicle using only surplus solar energy. The automation dynamically starts, stops, and adjusts the charging current based on real-time solar production and grid consumption. I’m still actively refining it and will share updates and findings in this thread. The automations were designed with the help of Claude.ai, which I used as an AI assistant to iteratively write and debug the YAML.
Goal
The goal is to automatically charge my EV only when there is sufficient surplus solar energy being fed back to the grid, minimizing grid consumption and maximizing self-consumption of solar power. The charging current is adjusted every 2 minutes based on available surplus power, scaling from 6A up to 16A depending on how much solar energy is available.
Hardware used
- EV: Skoda Enyaq (2021)
- EV charger: Shelly Topac (single/three-phase, controllable via Home Assistant)
- Solar panels: 6kW peak capacity
- Smart meter reader: DSMR P1 meter (connected to Home Assistant)
- Home Assistant running on local hardware
Software / Integrations used
- Home Assistant (automation platform)
- MySkoda integration (github.com/skodaconnect/homeassistant-myskoda) — provides vehicle status, battery level, charging status, and control
- Shelly integration (built-in HA integration) — controls the Shelly Topac EV charger
- DSMR Reader integration — reads real-time grid feed-in and consumption from the P1 smart meter
- Claude.ai — used as an AI assistant to help design, write, and iteratively refine the YAML automations
How it works
The project consists of three parts:
1. A template sensor that calculates the net grid feed-in:
net_feedin = solar_production - grid_consumption
This is necessary because the DSMR P1 integration in Home Assistant reports production and consumption as separate sensors. Since solar panels only feed back on one phase in my setup, the individual sensors can show simultaneous production and consumption, while the physical meter shows the correct net value. The template sensor corrects for this.
2. Start/Stop automation — checks every 6 minutes (and on trigger):
- Starts charging when net feed-in ≥ threshold for 2 minutes
- Stops charging when net consumption > 0.4kW for 5 minutes (and charger has been on for at least 3 minutes)
- Supports both single-phase (with adapter plug) and three-phase charging via a dropdown helper
3. Adjust current automation — runs every 2 minutes while charging:
- Calculates available surplus = net feed-in + current charger power (since the charger’s own consumption disappears from the feed-in sensor once it starts)
- Maps available surplus to the optimal charge current using a fixed power table (6A–16A)
- Only adjusts if the new value differs from the current setting and the charger has been running for at least 2 minutes
Power table used (three-phase)
| Ampere | Power (kW) |
|---|---|
| 6A | 4.0 |
| 7A | 4.4 |
| 8A | 5.0 |
| 9A | 5.7 |
| 10A | 6.4 |
| 11A | 7.1 |
| 12A | 7.7 |
| 13A | 8.2 |
| 14A | 8.9 |
| 15A | 9.5 |
| 16A | 10.2 |
Single-phase power table (with adapter plug)
| Ampere | Power (kW) |
|---|---|
| 6A | 1.38 |
| 7A | 1.61 |
| 8A | 1.84 |
| 9A | 2.07 |
| 10A | 2.30 |
| 11A | 2.53 |
| 12A | 2.76 |
| 13A | 2.99 |
| 14A | 3.22 |
| 15A | 3.45 |
| 16A | 3.68 |
Challenges
The biggest challenge has been reliably waking up the Skoda Enyaq from sleep mode via Home Assistant. The MySkoda integration communicates with the vehicle via the cloud, and when the car is asleep the sensor values (charging status, cable connected, etc.) are stale and unreliable. This causes several issues:
- The “cable connected” binary sensor reports
offeven when the cable is physically plugged in - The “charging status” sensor shows outdated values
- The wakeup button (
button.ev_vehicle_wakeup) sends the wakeup signal but the car takes variable time to respond — sometimes 30 seconds, sometimes over a minute, sometimes not at all without opening the Skoda app - If the Shelly charger tries to start before the car is fully awake, the car rejects the handshake and the charger switches off within 15 seconds
Workarounds currently in use:
- Retry mechanism — the automation tries to turn on the Shelly charger up to 3 times with 15-second intervals
- Charge limit trick — temporarily changing the charge limit from 80% to 90% seems to trigger a cloud refresh and wake the car more reliably. The automation sets it to 90% at the start of the charging sequence, waits 60 seconds, then sets it back to 80% after a successful start
- Single-phase adapter — for days with lower solar production (e.g. winter), a physical single-phase adapter plug can be connected. The automation detects which mode is active by checking whether phase B and C show zero power, and switches to the appropriate power table and start threshold automatically. A dropdown helper allows manual override
Still working on:
- Making the wakeup sequence more reliable without needing to open the Skoda app
- Fine-tuning thresholds for single-phase mode
- Testing behaviour over winter months with lower solar production
Helpers needed
| Helper | Type | Entity ID | Purpose |
|---|---|---|---|
| Solar charging active | Toggle (boolean) | input_boolean.ev_charging_active |
Master on/off switch |
| Charger mode | Dropdown (select) | input_select.ev_charger_mode |
1-phase / 3-phase / Auto |
| Net feed-in | Template sensor | sensor.net_feedin |
Calculated net feed-in (kW) |
Note: For daily charged energy I use the built-in energy sensor from the Shelly Topac charger (
sensor.ev_charger_energy_today), which tracks session and daily totals natively. No utility meter helper needed.
The automations
Template sensor (add via Helper → Template sensor, or in configuration.yaml):
template:
- sensor:
- name: "Net feed-in"
unique_id: net_feedin
unit_of_measurement: kW
state_class: measurement
device_class: power
state: >
{{ states('sensor.electricity_meter_production') | float(0) -
states('sensor.electricity_meter_consumption') | float(0) }}
Start/Stop automation:
alias: EV Solar charging - Start/Stop
description: >
Starts charging when net feed-in is sufficient (2 min), stops when net
consumption exceeds 0.4kW for 5 minutes. Wakes car via charge limit trick.
triggers:
- trigger: time_pattern
minutes: /6
- trigger: numeric_state
entity_id: sensor.net_feedin
above: 1.1
for:
minutes: 2
- trigger: numeric_state
entity_id: sensor.net_feedin
below: -0.4
for:
minutes: 5
variables:
net_feedin: "{{ states('sensor.net_feedin') | float(0) }}"
production: "{{ states('sensor.electricity_meter_production') | float(0) }}"
consumption: "{{ states('sensor.electricity_meter_consumption') | float(0) }}"
charger_power: "{{ states('sensor.ev_charger_power') | float(0) }}"
charger_on: "{{ is_state('switch.ev_charger_switch', 'on') }}"
is_single_phase: >
{% if is_state('input_select.ev_charger_mode', '1-phase') %}
true
{% elif is_state('input_select.ev_charger_mode', '3-phase') %}
false
{% else %}
{% if is_state('switch.ev_charger_switch', 'on') %}
{{ states('sensor.ev_charger_phase_b_power') | float(0) < 0.1 and
states('sensor.ev_charger_phase_c_power') | float(0) < 0.1 }}
{% else %}
{{ net_feedin < 3.3 }}
{% endif %}
{% endif %}
start_threshold: >
{{ 1.1 if is_single_phase == 'true' or is_single_phase == true else 3.6 }}
actions:
- choose:
- conditions:
- condition: template
value_template: "{{ charger_on }}"
- condition: template
value_template: "{{ net_feedin < -0.4 }}"
- condition: template
value_template: >
{{ (now() -
states.switch.ev_charger_switch.last_changed).total_seconds()
> 180 }}
sequence:
- action: switch.turn_off
target:
entity_id: switch.ev_charger_switch
- action: persistent_notification.create
data:
title: "EV charging stopped"
message: >
Charging stopped - net consumption was
{{ (net_feedin * -1) | round(2) }}kW
(production: {{ production }}kW,
consumption: {{ consumption }}kW).
- conditions:
- condition: template
value_template: "{{ not charger_on }}"
- condition: template
value_template: "{{ net_feedin >= start_threshold | float }}"
- condition: state
entity_id: input_boolean.ev_charging_active
state: "on"
sequence:
- action: button.press
target:
entity_id: button.ev_vehicle_wakeup
- action: number.set_value
target:
entity_id: number.ev_charge_limit
data:
value: 90
- delay:
seconds: 60
- action: number.set_value
target:
entity_id: number.ev_charger_current_limit
data:
value: 6
- action: switch.turn_on
target:
entity_id: switch.ev_charger_switch
- delay:
seconds: 15
- if:
- condition: state
entity_id: switch.ev_charger_switch
state: "off"
then:
- action: switch.turn_on
target:
entity_id: switch.ev_charger_switch
- delay:
seconds: 15
- if:
- condition: state
entity_id: switch.ev_charger_switch
state: "off"
then:
- action: switch.turn_on
target:
entity_id: switch.ev_charger_switch
- delay:
seconds: 15
- if:
- condition: state
entity_id: switch.ev_charger_switch
state: "on"
then:
- action: number.set_value
target:
entity_id: number.ev_charge_limit
data:
value: 80
- action: persistent_notification.create
data:
title: "EV charging started"
message: >
Charging at 6A in
{{ '1-phase' if is_single_phase == 'true'
or is_single_phase == true else '3-phase' }} mode.
Net feed-in: {{ net_feedin | round(2) }}kW
(production: {{ production }}kW,
consumption: {{ consumption }}kW).
else:
- action: number.set_value
target:
entity_id: number.ev_charge_limit
data:
value: 80
- action: persistent_notification.create
data:
title: "EV charging failed"
message: >
Could not start charging after 3 attempts.
Please check the vehicle.
default: []
mode: single
Adjust current automation:
alias: EV Solar charging - Adjust current
description: >
Adjusts charging current every 2 minutes based on available solar surplus.
Only active when charger is running for at least 2 minutes.
triggers:
- trigger: time_pattern
minutes: /2
variables:
net_feedin: "{{ states('sensor.net_feedin') | float(0) }}"
charger_power: "{{ states('sensor.ev_charger_power') | float(0) }}"
charger_on: "{{ is_state('switch.ev_charger_switch', 'on') }}"
current_ampere: "{{ states('number.ev_charger_current_limit') | int(6) }}"
available: "{{ net_feedin + charger_power }}"
is_single_phase: >
{% if is_state('input_select.ev_charger_mode', '1-phase') %}
true
{% elif is_state('input_select.ev_charger_mode', '3-phase') %}
false
{% else %}
{{ states('sensor.ev_charger_phase_b_power') | float(0) < 0.1 and
states('sensor.ev_charger_phase_c_power') | float(0) < 0.1 }}
{% endif %}
new_ampere: >
{% if is_single_phase == 'true' or is_single_phase == true %}
{% set table = [
{'a': 16, 'kw': 3.68}, {'a': 15, 'kw': 3.45}, {'a': 14, 'kw': 3.22},
{'a': 13, 'kw': 2.99}, {'a': 12, 'kw': 2.76}, {'a': 11, 'kw': 2.53},
{'a': 10, 'kw': 2.30}, {'a': 9, 'kw': 2.07}, {'a': 8, 'kw': 1.84},
{'a': 7, 'kw': 1.61}, {'a': 6, 'kw': 1.38}
] %}
{% else %}
{% set table = [
{'a': 16, 'kw': 10.2}, {'a': 15, 'kw': 9.5}, {'a': 14, 'kw': 8.9},
{'a': 13, 'kw': 8.2}, {'a': 12, 'kw': 7.7}, {'a': 11, 'kw': 7.1},
{'a': 10, 'kw': 6.4}, {'a': 9, 'kw': 5.7}, {'a': 8, 'kw': 5.0},
{'a': 7, 'kw': 4.4}, {'a': 6, 'kw': 4.0}
] %}
{% endif %}
{% set ns = namespace(result=6) %}
{% for step in table %}
{% if available >= step.kw and ns.result == 6 %}
{% set ns.result = step.a %}
{% endif %}
{% endfor %}
{{ ns.result }}
actions:
- condition: template
value_template: "{{ charger_on }}"
- condition: template
value_template: "{{ new_ampere | int != current_ampere | int }}"
- condition: template
value_template: >
{{ (now() -
states.switch.ev_charger_switch.last_changed).total_seconds()
> 120 }}
- action: number.set_value
target:
entity_id: number.ev_charger_current_limit
data:
value: "{{ new_ampere | int }}"
mode: single
Dashboard card:
type: vertical-stack
cards:
- type: heading
heading: EV Solar Charging
- type: glance
title: Status
entities:
- entity: switch.ev_charger_switch
name: Charging
- entity: number.ev_charger_current_limit
name: Ampere
- entity: sensor.ev_charger_power
name: Power
- entity: sensor.ev_battery_percentage
name: Battery
- type: entities
title: Solar & Grid
entities:
- entity: sensor.electricity_meter_production
name: Solar feed-in
- entity: sensor.electricity_meter_consumption
name: Grid consumption
- entity: sensor.net_feedin
name: Net feed-in
- entity: input_boolean.ev_charging_active
name: Solar charging active
- entity: input_select.ev_charger_mode
name: Charger mode
- type: glance
title: Phase power
entities:
- entity: sensor.ev_charger_phase_a_power
name: Phase A
- entity: sensor.ev_charger_phase_b_power
name: Phase B
- entity: sensor.ev_charger_phase_c_power
name: Phase C
- type: entities
title: Vehicle
entities:
- entity: sensor.ev_charging_status
name: Charging status
- entity: sensor.ev_range
name: Range
- entity: number.ev_charge_limit
name: Charge limit (%)
- entity: device_tracker.ev_location
name: Location
- type: history-graph
title: Power today
hours_to_show: 24
entities:
- entity: sensor.electricity_meter_production
name: Solar feed-in
- entity: sensor.ev_charger_power
name: Charger power
- entity: sensor.ev_charger_phase_a_power
name: Phase A
- entity: sensor.ev_charger_phase_b_power
name: Phase B
- entity: sensor.ev_charger_phase_c_power
name: Phase C
- type: entities
title: Energy
entities:
- entity: sensor.ev_charger_energy_today
name: Energy charged today
- entity: sensor.ev_charger_session_energy
name: Current session energy
- entity: sensor.ev_charger_session_duration
name: Current session duration
- type: entities
title: Controls
entities:
- entity: switch.ev_charger_switch
name: Manual charger on/off
- entity: number.ev_charger_current_limit
name: Manual ampere
- entity: button.ev_vehicle_wakeup
name: Wake up vehicle
- entity: button.ev_charger_restart
name: Restart charger
Work in progress / known issues
- Wakeup reliability is the main pain point — still testing the charge limit trick (80% → 90% → 80%)
- The retry mechanism (up to 3 attempts with 15-second intervals) helps but is not foolproof
- The MySkoda integration occasionally reports stale sensor values when the car is asleep — working around this but not fully solved yet
- Single-phase mode thresholds may need further tuning in winter
- Will report back with findings as the season changes
I’ll keep this post updated as I refine the setup! I’ll add photo’s of the ev charger setup and dashboard asap (I don’t know when..busy familylife.. ![]()