浪人
DE|EN
EVCC Foundation: The Setup Before Any Assistant Gets Involved
tech

EVCC Foundation: The Setup Before Any Assistant Gets Involved

Back to Blog
3 min read

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.

Grid and PV values simultaneously in the EVCC web UI

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.

The go-e wallbox at the carport

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.

Battery level display in the EVCC UI via Tesla Fleet API

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.

Loadpoint map “Carport” during live operation

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.