Solar-optimised EV charging manager. Acts as an OCPP 1.6 Central System (server) that your wallbox connects to, and uses live weather data plus your PV specification to automatically regulate charging current so you only use solar surplus.
No inverter connection required — solar production is estimated from irradiance data (Open-Meteo) and your array parameters (kWp, tilt, azimuth).
Open-Meteo API ──► Solar estimate (W)
│
▼
Buffer (configurable %)
│
▼
Wallbox ──OCPP 1.6──► AutoCharge ──► SetChargingProfile / RemoteStart / RemoteStop
- Solar mode — charges at maximum available solar surplus, pauses when below threshold; optionally switches between 1-phase and 3-phase automatically
- Fixed mode — charges at a set kW (entered directly in kW, converted to amps per phase)
- Off — no charging commands sent
| Component | Minimum |
|---|---|
| Python | 3.12 |
| Node.js | 20 |
| npm | 10 |
| Docker | 24 (optional, for deployment) |
git clone <repo-url>
cd autocharge
bash start-dev.shThis will:
- Create a Python venv and install backend dependencies
- Install frontend npm dependencies
- Start backend on http://localhost:8000 (hot-reload)
- Start frontend on http://localhost:5173 (Vite HMR)
- Create
data/config.jsonanddata/autocharge.dbon first run
# Kill everything on the relevant ports
pkill -f "uvicorn main:app"; pkill -f "vite"
# Restart
bash start-dev.shOr in one line:
pkill -f "uvicorn main:app"; pkill -f "vite"; sleep 1; bash start-dev.shpkill -f "uvicorn main:app"
cd backend
CONFIG_PATH=../data/config.json DB_PATH=../data/autocharge.db \
.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 --reloadpkill -f "vite"
cd frontend
npm run devlsof -i :8000 # backend
lsof -i :5173 # frontend dev server
lsof -i :9000 # OCPP WebSocket serverdocker compose up --build -d- Frontend: http:// (port 80)
- Backend API: http://:8000
- OCPP WebSocket: ws://:9000/
Configuration and database are stored in a Docker volume (autocharge_data), so they survive container restarts and rebuilds.
git pull
docker compose up --build -ddocker compose downdocker compose down -vdocker compose logs -f # all services
docker compose logs -f backend # backend only
docker compose logs -f frontend # nginx onlydocker compose restart backend
docker compose restart frontenddocker compose exec backend bash
docker compose exec frontend shInstall dependencies:
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate /etc/systemd/system/autocharge-backend.service:
[Unit]
Description=AutoCharge Backend
After=network-online.target
Wants=network-online.target
[Service]
User=<your-user>
WorkingDirectory=/opt/autocharge/backend
Environment=CONFIG_PATH=/opt/autocharge/data/config.json
Environment=DB_PATH=/opt/autocharge/data/autocharge.db
ExecStart=/opt/autocharge/backend/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now autocharge-backend
sudo systemctl status autocharge-backendBuild:
cd frontend
npm install
npm run build # output: frontend/dist/Copy dist to nginx web root or configure nginx to serve it:
server {
listen 80;
root /opt/autocharge/frontend/dist;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:8000;
}
location /ws {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
location / {
try_files $uri $uri/ /index.html;
}
}Rebuild frontend after code changes:
cd frontend && npm run build
sudo systemctl reload nginx-
In your wallbox settings, find the OCPP configuration section
-
Set the OCPP Central System URL to:
ws://<server-ip>:9000/<wallbox-id>Example:
ws://192.168.1.100:9000/wallbox1 -
<wallbox-id>can be any name you choose (e.g.wallbox1,gemini, …) -
Set OCPP protocol version to OCPP 1.6 (OCPP 1.6-J / JSON)
-
Save and reboot the wallbox
The wallbox ID will appear under Ladepunkte in the UI once it connects.
go-e Gemini / HOME+: Settings → OCPP → Server URL
Port 9000 must be reachable from the wallbox. If AutoCharge runs on a different host than your router, make sure port 9000 is not blocked by a firewall.
All settings are available in the UI under Einstellungen. They are saved to data/config.json.
| Section | Key settings |
|---|---|
| Location | GPS coordinates (map picker + address search), timezone |
| PV arrays | Name, kWp, tilt (°), azimuth (°), efficiency — add multiple arrays |
| Charging params | Buffer %, start/stop thresholds, min/max amps, phases, fixed kW, phase switching, go-e IP, update interval |
| System | Weather update interval, OCPP port |
| Value | Direction |
|---|---|
| 0° | North |
| 90° | East |
| 180° | South (optimal in northern hemisphere) |
| 270° | West |
Enter the desired charging power in kW. The UI shows the equivalent amps at the configured phase count and 230 V, e.g. 5.52 kW ≈ 8 A @ 3×230 V.
Set 10–15 % buffer to account for forecast uncertainty and avoid drawing from the grid on partly cloudy days.
Relevant for 3-phase chargers (e.g. go-e Gemini 11 kW) where you want to charge on a single phase when solar surplus is too low for 3-phase but still above the 1-phase minimum.
| Available power | Behaviour |
|---|---|
| < 1 380 W (6 A × 1ph) | Charging paused |
| 1 380 – 4 140 W | 1-phase, up to 16 A |
| > 4 140 W (6 A × 3ph) | 3-phase, up to 16 A |
Thresholds scale with your min_amps setting (default 6 A).
In Einstellungen → Ladeparameter turn on Automatische Phasenumschaltung.
Phase switching is attempted via (in order):
- OCPP
ChangeConfiguration(NumberOfPhases)— standard, few chargers support it - go-e local HTTP API (
/api/set?psm=1/psm=2) — set the go-e IP in the config field that appears when switching is enabled - State-only update — works if the charger is in auto-PSM mode and follows the charging profile
A 3-minute cooldown between switches prevents rapid toggling on fluctuating solar.
When a phase switch is needed while charging, AutoCharge automatically stops the session, switches phases, then restarts.
psm |
Meaning |
|---|---|
| 0 | Auto (charger decides) |
| 1 | Force 1-phase |
| 2 | Force 3-phase |
- Weather / irradiance: Open-Meteo — free, no API key required, updated every 15 min by default
- Solar calculation: Pure-Python PV model (solar geometry + isotropic sky model), no external libraries
- Map / geocoding: OpenStreetMap / Nominatim
autocharge/
├── backend/
│ ├── main.py # FastAPI app, background loops, REST + WebSocket
│ ├── config.py # Pydantic models, config load/save
│ ├── charging.py # Solar charging algorithm
│ ├── ocpp_server.py # OCPP 1.6 Central System
│ ├── solar.py # Weather fetch + PV power estimation
│ ├── database.py # SQLite history (aiosqlite)
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── pages/
│ │ ├── Dashboard.tsx # Live stats, mode selector, forecast
│ │ ├── Chargers.tsx # OCPP charger details + manual control
│ │ ├── History.tsx # Power history chart
│ │ ├── Config.tsx # Settings form
│ │ └── Logs.tsx # Live backend log viewer
│ └── components/
│ ├── Layout.tsx # Nav, dark/light toggle, shutdown
│ └── MapPicker.tsx # Leaflet map for location
├── Dockerfile.backend
├── Dockerfile.frontend
├── docker-compose.yml
├── nginx.conf
├── start-dev.sh # One-command dev start
└── data/ # config.json + autocharge.db (gitignored)
| Port | Service |
|---|---|
| 80 | Frontend (production / Docker) |
| 5173 | Frontend (dev, Vite) |
| 8000 | Backend API + WebSocket |
| 9000 | OCPP WebSocket server (wallbox connects here) |
Frontend not accessible
pkill -f "uvicorn main:app"; pkill -f "vite"; sleep 1; bash start-dev.shWallbox not appearing in UI
- Check the OCPP URL includes the wallbox ID:
ws://ip:9000/wallbox1notws://ip:9000 - Verify port 9000 is reachable:
telnet <server-ip> 9000 - Check Logs tab in the UI for connection events
kWh counter shows 0
- AutoCharge sets an energy baseline from the first MeterValues message after connection. If no MeterValues arrive within 30 s, check that your wallbox has
MeterValueSampleIntervalenabled (AutoCharge sends this automatically after BootNotification).
Solar estimate seems wrong
- Double-check azimuth (180° = facing south in northern hemisphere) and tilt
- Verify the GPS location is correct on the map
- Check weather data age in the Dashboard — if stale, the backend may have lost internet access
Docker: frontend can't reach backend
- The backend runs with
network_mode: host; the frontend nginx useshost.docker.internalto reach it - On Linux,
extra_hosts: host.docker.internal:host-gatewayis set indocker-compose.yml— requires Docker Engine ≥ 20.10
View backend logs (Docker)
docker compose logs -f backendView backend logs (dev) Open the Logs tab in the UI — all backend log messages appear there in real time.
Current (A) shows 0.0 in the charger card
Some chargers (including go-e) send only per-phase Current.Import values (L1/L2/L3) without an aggregate. AutoCharge reads the L1 value in that case and auto-detects the active phase count from the number of phases carrying current (> 0.5 A).
Phase switching not working
- Make sure the go-e IP is entered in the config (Settings → Ladeparameter)
- Check the Logs tab for
go-e phase switchmessages - If the charger is in auto-PSM mode already, disable phase switching in AutoCharge and let the charger handle it
- Phase switches only happen in solar mode, not fixed or off