Posted in

Smart Charging My EVs with EVCC

I’ve been driving an electric car for over two years now and I absolutely love it. The driving experience is great, charging at home is convenient, and combining it with solar power makes it even better.

When I got my first EV, I immediately installed a home charger. I also have a dynamic energy contract, but I mostly charged at work or during the night when electricity prices were low.

Recently, we decided to replace our second ICE car with another EV. At the same time, changes in Dutch regulations, such as the introduction of ERE certificates and the expected phase-out of the Salderingsregeling, made me rethink our charging strategy. With two EVs, solar panels, and dynamic pricing, I wanted to optimize charging as much as possible.

Being an IT and automation enthusiast, this was the perfect weekend project. Enter evcc.

The Goal

My requirements were straightforward:

  • Prioritize Solar: Use maximum PV surplus for charging.
  • Smart Pricing: Factor in dynamic hourly energy rates.
  • Reliability: Ensure both cars are ready for the morning commute.
  • Safety: Prevent blowing the main fuse (Load Management).

Installation & Environment

evcc is incredibly flexible; it supports Docker, Home Assistant add-ons, and bare metal. I chose to run it in an LXC container on Proxmox for better resource management. While the installation is outside the scope of this post, the official documentation is excellent.

Once the service was up, the real fun began: the configuration.

Configuration

The Charger – Alfen Single Line Pro

To allow evcc to control my Alfen Single Line Pro, I enabled Modbus communication via the MyEve app:

  1. AdvancedSmart ChargingActive Load Balancing: Set Protocol to Energy Management System.
  2. AdvancedSmart Charging Energy Management: Set Mode to Socket and ValidityTime to 300s.

If you use a different charger, check the EVCC charger documentation: https://docs.evcc.io/en/docs/devices/chargers

In the evcc GUI, I applied the following key settings:

Default Mode: Solar
I only want to charge using solar power by default. If I need a certain State of Charge (SOC) in the morning, the planner takes care of it.

Solar Mode: Custom (not Maximum)
My solar panels don’t always produce enough power for stable charging. Since single-phase charging requires about 1.4 kW to start, I configured:

  • Enable grid power at: -750 W
  • Disable grid power at: 750 W

This means:

  • Charging starts when at least 750 W of solar surplus is available.
  • evcc may supplement with grid power if needed to reach the minimum charging level.

Vehicle Detection: Automatic
Vehicle information is only updated while charging; no need to poll when the car is disconnected.

Circuit configuration is handled later under load management.

Vehicles

I have a Kia EV6 and a Hyundai Inster. Setting them up in the GUI is straightforward: just select the brand and model.

I configured the actual battery capacities and enabled three-phase charging for both vehicles to ensure they can take full advantage of high-power charging windows.

Vehicle documentation: https://docs.evcc.io/en/docs/devices/vehicles

Solar Panels

My GoodWe GW2000-NS inverter does not integrate directly with evcc. However, since it was already integrated into Home Assistant, I simply pointed evcc to the Home Assistant pv_power sensor.

If you use a different inverter, check: https://docs.evcc.io/en/docs/devices/meters

Grid Meter

For accurate load balancing, you need real-time grid data. I used DSMR-Reader and connected evcc directly to its API. This provides clean three-phase data (import/export and current per phase).

This data is essential for load management and ensures the household stays within the 3×25A limit.

power:
  source: calc
  add:
    - source: http
      uri: http://<>:7777/api/v2/consumption/electricity-live
      method: GET # default HTTP method
      headers:
        - content-type: application/json
        - X-AUTHKEY: YOUR_KEY
      insecure: true # set to true to trust self-signed certificates
      jq: .|{currently_delivered}| join(" ")
      scale: 1
      timeout: 10s
    - source: http
      uri: http://<>:7777/api/v2/consumption/electricity-live
      method: GET # default HTTP method
      headers:
      - content-type: application/json
      - X-AUTHKEY: YOUR_KEY
      insecure: true # set to true to trust self-signed certificates
      jq: .|{currently_returned}| join(" ")
      scale: -1
      timeout: 10s
energy:
  source: calc
  add:
    - source: http
      uri: http://<>:7777/api/v2/consumption/today
      method: GET # default HTTP method
      headers:
        - content-type: application/json
        - X-AUTHKEY: YOUR_KEY
      insecure: true # set to true to trust self-signed certificates
      jq: .|{electricity_merged}| join(" ")
      scale: 1
      timeout: 10s
    - source: http
      uri: http://<>:7777/api/v2/consumption/today
      method: GET # default HTTP method
      headers:
        - content-type: application/json
        - X-AUTHKEY: YOUR_KEY
      insecure: true # set to true to trust self-signed certificates
      jq: .|{electricity_returned_merged}| join(" ")
      scale: -1
      timeout: 10s
currents:
  - source: http
    uri: http://<>:7777/api/v2/datalogger/dsmrreading?limit=1&ordering=-timestamp
    method: GET # default HTTP method
    headers:
      - content-type: application/json
      - X-AUTHKEY: YOUR_KEY
    insecure: true # set to true to trust self-signed certificates
    jq: .results[]|{phase_power_current_l1}| join(" ")
    scale: 1
    timeout: 10s
  - source: http
    uri: http://<>:7777/api/v2/datalogger/dsmrreading?limit=1&ordering=-timestamp
    method: GET # default HTTP method
    headers:
      - content-type: application/json
      - X-AUTHKEY: YOUR_KEY
    insecure: true # set to true to trust self-signed certificates
    jq: .results[]|{phase_power_current_l2}| join(" ")
    scale: 1
    timeout: 10s
  - source: http
    uri: http://<>:7777/api/v2/datalogger/dsmrreading?limit=1&ordering=-timestamp
    method: GET # default HTTP method
    headers:
      - content-type: application/json
      - X-AUTHKEY: YOUR_KEY
    insecure: true # set to true to trust self-signed certificates
    jq: .results[]|{phase_power_current_l3}| join(" ")
    scale: 1
    timeout: 10s
  

Tariffs (Dynamic Pricing + Forecasting)

This is where evcc outshines standard smart chargers. By using the epexprijzen.nl API, evcc knows the hourly prices for the next 24 hours.

I configured the following:

  • Grid Price & Feed-in Forecasts: To know when it’s cheaper to charge than to sell.
  • Solar Forecast: Using forecast-solar to predict how much “free” energy is coming tomorrow.

With this data, evcc doesn’t just react; it plans. It knows if it should wait for a sunny afternoon or a cheap 3:00 AM window.

currency: EUR

grid:
  type: custom
  forecast:
    source: http
    uri: https://epexprijzen.nl/api/v1/prices/zonneplan/hourly
    jq: '.today + .tomorrow | map({"start": (.t | strptime("%Y-%m-%dT%H:%M:%S%z") | strftime("%Y-%m-%dT%H:%M:%SZ")), "end": (.t | strptime("%Y-%m-%dT%H:%M:%S%z") | mktime + 3600 | strftime("%Y-%m-%dT%H:%M:%SZ")),  "value": .price}) | tostring'

feedin:
  type: custom
  forecast:
    source: http
    uri: https://epexprijzen.nl/api/v1/prices/zonneplan/hourly
    jq: '.today + .tomorrow | map({"start": (.t | strptime("%Y-%m-%dT%H:%M:%S%z") | strftime("%Y-%m-%dT%H:%M:%SZ")), "end": (.t | strptime("%Y-%m-%dT%H:%M:%S%z") | mktime + 3600 | strftime("%Y-%m-%dT%H:%M:%SZ")),  "value": .price}) | tostring'

co2:
  type: template
  template: ned
  apiKey: YOUR_API_KEY

solar:
  type: template
  template: forecast-solar
  lat: YOUR_LAT
  lon: YOUR_LONG
  az: 184
  dec: 25
  kwp: 2.16

Tariff documentation: https://docs.evcc.io/en/docs/tariffs

Load Management

In the Netherlands, a standard residential connection is 3x25A. If you are charging an EV at 11 kW while the heat pump and oven are running, you might blow the main fuse.

evcc handles this through a Circuit configuration. I defined my main circuit with a maxCurrent: 25.

- name: main
  title: Hoofdcircuit
  maxCurrent: 25
  meter: db:8

If the house consumption spikes, evcc throttles the chargers within seconds. This “Active Load Balancing” is the only way to safely run a multi-EV household on a standard Dutch grid connection.

Charging Plan

This is where the “Smart” in Smart Charging actually happens. While the default mode is Solar, I have hard requirements for the next morning.

I created a Repeating Plan for 100% State of Charge (SOC) by 06:30 AM. EVCC’s algorithm then looks at:

  1. Your required energy: How many kWh are needed to reach the target?
  2. Solar Forecast: Is there a burst of sun expected before the deadline?
  3. Dynamic Prices: When are the cheapest hours (or negative price windows) between now and 06:30 AM?

It then schedules charging sessions into those specific “cheapest” slots. It means that it can stop and start charging in between. If you prefer to charge a full bock, make sure to set it to continuous so it plans a full block rather than cheapest moments.

If you have multiple vehicles, configure a plan per vehicle.

Charging When Prices Are Low

In the evcc interface, you can configure a price limit that determines when the system should switch to aggressive charging. Whenever the electricity price falls below this threshold, evcc automatically charges at maximum speed. This feature is especially useful during periods of very low or even negative electricity prices, allowing you to take full advantage of favorable market conditions.

Conclusion

The results have been fantastic. The setup provides a high WAF (Wife Acceptance Factor): my wife just plugs in the car, and the system handles the complexity.

We’ve achieved maximum automation, optimized our costs under a dynamic contract, and made the most of our solar investment. If you’re into smart homes and energy management, evcc is the missing link.

Leave a Reply

Your email address will not be published. Required fields are marked *