
EVCC Foundation: The Setup Before Any Assistant Gets Involved
EVCC Foundation: The Setup Before Any Assistant Gets Involved
I’ve already written extensively about the old go-e wallbox and its revival. What remained somewhat in the background in that story was the actual foundation underneath it: EVCC itself, and what the configuration looks like that turns an inverter, a meter, and a car into a functioning charging system. That’s exactly the starting point for this new series - and the series has a second purpose: it documents not only what was configured, but how - first completely manually, later with an AI assistant on board. But one thing at a time.
What EVCC Actually Does
EVCC is an open-source “solar charging controller.” It sits between the inverter, grid meter, wallbox, and optionally the car itself, and decides in real-time how much current the car receives - coordinated with the actual PV surplus instead of simply drawing grid power as soon as a cable is plugged in. Technically: a single Go binary or Docker image, a YAML config, a lean web UI for daily use.
This post describes the state of things as it existed before any AI assistant was involved at all - the pure, manually configured foundation on which the later parts build.
Where It Runs
EVCC runs as a single Docker container on the homelab server, which already hosts a handful of other self-hosted services:
# docker-compose.yml (structure, not the actual file)
services:
evcc:
image: evcc/evcc:0.312.1
volumes:
- ./evcc.yaml:/etc/evcc.yaml:ro # read-only bind mount
- ./data:/data # writable: SQLite DB, cache
ports:
- "7070:7070"
restart: unless-stopped
Two details that come up again later in the series:
- The config file is mounted read-only. EVCC cannot rewrite its own config from inside - any change means: edit the file on the host, restart the container. This is a deliberate design decision, not a limitation of EVCC itself.
- The SQLite database lives in its own writable volume, not at the default path in the container - otherwise it would end up in the container’s home directory and disappear on every rebuild.
The instance is accessible internally via <EVCC_HOSTNAME>:7070.
The Config, Piece by Piece
Network and Basics
network:
schema: http
host: <EVCC_HOSTNAME>
port: 7070
interval: 30s # how often meters/wallbox are queried and re-evaluated
database:
type: sqlite
dsn: /data/evcc.db
log: info
Sponsorship
sponsortoken: <SPONSOR_TOKEN>
EVCC is free and open, but some device drivers - in this case the go-e driver - require an active sponsor token. GitHub sponsorship of the project provides one: a long-lived JWT that goes directly into the config.
Meters - Grid and PV, via Modbus/SunSpec
The inverter is a Fronius Verto Plus 15 kW. EVCC talks to it via Modbus TCP using the SunSpec standard - a cross-manufacturer protocol that most serious solar inverters support, instead of a proprietary vendor API:
meters:
- name: netz
type: template
template: fronius-vertoplus
usage: grid
host: <INVERTER_IP>
port: 502
id: 200 # Modbus device ID for the grid meter function
- name: pv_dach
type: template
template: fronius-vertoplus
usage: pv
host: <INVERTER_IP>
port: 502
maxacpower: 15000
Both entries point to the same physical device and the same TCP connection - the Verto Plus provides multiple logical SunSpec “models” over a single Modbus connection, and usage: grid vs. usage: pv simply selects which model EVCC reads. The grid meter function uses its own Modbus ID (200), the PV function (inverter itself) the default ID (1, implicit here). This separation - one physical connection, multiple logical views via usage: and partly via the device ID - becomes important later in the series once a battery storage system is added.

Wallbox
chargers:
- name: goe_carport
type: go-e
uri: http://<CHARGER_IP>
The go-e wallbox (hardware V2, “CM-02”) is controlled via its local HTTP API - no cloud detour needed for charging control.

Vehicle - Read-Only Access to Battery Level via Tesla Fleet API
vehicles:
- name: tesla_model3
type: template
template: tesla
title: <VEHICLE_TITLE>
clientId: <TESLA_CLIENT_ID>
accessToken: <TESLA_ACCESS_TOKEN>
refreshToken: <TESLA_REFRESH_TOKEN>
vin: <VEHICLE_VIN>
capacity: 79
cache: 15m
This entry is deliberately minimal: it reads exclusively the car’s battery level via the Tesla Fleet API - but sends no start/stop or current limit commands to the car. The actual charging control runs completely through the go-e box above. Two reasons for this:
- The go-e wallbox already handles charging control reliably and locally at the hardware level - a detour through Tesla’s cloud API adds nothing here.
- Tesla’s Fleet API requires its own registered developer app with approved OAuth redirect/origin. A generic third-party app doesn’t fit there, so a dedicated app was registered specifically for this - its client ID is listed above under
clientId.

Site and Loadpoint
site:
title: <SITE_NAME>
meters:
grid: netz
pv:
- pv_dach
loadpoints:
- title: Carport
charger: goe_carport
vehicle: tesla_model3
mode: "pv"
One loadpoint (“Carport”), one wallbox, one vehicle, in PV surplus mode - charging happens with excess solar power instead of on a fixed schedule, three-phase with roughly 6-16 A, depending on how much sun is available right now.

Status at the End of This Post
- EVCC is running, querying grid and PV every 30 seconds via SunSpec/Modbus.
- One wallbox, controlled locally via HTTP.
- One vehicle, battery level only, via a separately registered Tesla Fleet API app.
- One loadpoint, charging with PV surplus.
- No battery storage yet - that comes later in the series, and via a completely different approach than everything above.
In the next part, we’ll look at how these building blocks were actually created - completely manually, with a text editor and docker restart, before any assistant was even involved.