Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cd481a5ea | |||
| ca0f9ed1c3 |
@@ -1,10 +1,11 @@
|
||||
# EMS Configuration
|
||||
# EMS Configuration — house/son
|
||||
# Fill in all values marked with <...> before deploying.
|
||||
# All thresholds and timing parameters are configurable here.
|
||||
|
||||
prometheus:
|
||||
url: "http://192.168.0.23:9090"
|
||||
url: "http://<prometheus-ip>:9090"
|
||||
|
||||
# Metric names from the custom Viessmann exporter
|
||||
# Metric names from the custom Viessmann exporter (same as house/lutz)
|
||||
metrics:
|
||||
grid_power_exchange: "pcc_transfer_power_exchange_value" # positive = import, negative = export
|
||||
battery_soc: "ess_stateOfCharge_value" # 0-100%
|
||||
@@ -19,32 +20,33 @@ prometheus:
|
||||
# Shelly actuators
|
||||
shelly:
|
||||
sg_ready:
|
||||
ip: "192.168.42.90"
|
||||
ip: "<shelly-sg-ready-ip>"
|
||||
gen: 1 # Gen1: /relay/0?turn=on|off
|
||||
wallbox_a:
|
||||
ip: "192.168.42.185"
|
||||
ip: "<shelly-wallbox-a-ip>"
|
||||
gen: 2 # Gen2: /rpc/Switch.Set
|
||||
power_w: 2000
|
||||
power_w: 2000 # rated power of the wallbox (W)
|
||||
password: "" # set Shelly admin password if auth is enabled
|
||||
wallbox_b:
|
||||
ip: "192.168.42.51"
|
||||
ip: "<shelly-wallbox-b-ip>"
|
||||
gen: 2 # Gen2: /rpc/Switch.Set
|
||||
power_w: 4000
|
||||
power_w: 4000 # rated power of the wallbox (W)
|
||||
password: "" # set Shelly admin password if auth is enabled
|
||||
|
||||
# Viessmann API (write access for WW temperature)
|
||||
# Leave installation_id empty to disable WW boost (EMS runs without it)
|
||||
viessmann:
|
||||
token_file: "/etc/ems/viessmann-token.json" # OAuth2 refresh token
|
||||
client_id: "5d1235548dfaa01ce37db8f865d2df4d"
|
||||
installation_id: "1253124"
|
||||
gateway_serial: "7637415006796210"
|
||||
client_id: "<viessmann-oauth2-client-id>"
|
||||
installation_id: "" # leave empty to disable WW boost
|
||||
gateway_serial: "<gateway-serial>"
|
||||
device_id: "0"
|
||||
|
||||
# SOC thresholds — which consumers are allowed at which battery level
|
||||
soc_thresholds:
|
||||
block_all: 50 # below 50%: no consumers
|
||||
sg_ready_only: 70 # 50-70%: only SG-Ready (heating period)
|
||||
plus_wallbox_a: 90 # 70-90%: + Wallbox A (2kW)
|
||||
plus_wallbox_a: 90 # 70-90%: + Wallbox A
|
||||
all_consumers: 90 # above 90%: all consumers
|
||||
|
||||
# Hysteresis settings
|
||||
@@ -68,7 +70,7 @@ thresholds:
|
||||
strategic:
|
||||
forecast_high_kwh: 25
|
||||
forecast_mid_kwh: 15
|
||||
ww_base_c: 48 # normal WW setpoint (°C)
|
||||
ww_base_c: 48 # normal WW setpoint (°C) — adjust to your heat pump
|
||||
ww_boost_high_c: 5 # +5°C on high-forecast days (>25 kWh)
|
||||
ww_boost_mid_c: 3 # +3°C on medium-forecast days (>15 kWh)
|
||||
ww_window_start: "12:30" # WW boost only allowed from 12:30
|
||||
@@ -91,16 +93,16 @@ season:
|
||||
# PV forecast — forecast.solar (free, no API key required)
|
||||
forecast:
|
||||
enabled: true
|
||||
lat: 48.7839
|
||||
lon: 9.3889
|
||||
declination: 20 # panel tilt in degrees
|
||||
azimuth: 5 # degrees from south (south=0, west=90, east=-90)
|
||||
kwp: 7.0 # installed peak power
|
||||
fetch_interval: "4h" # re-fetch during the day (free tier: 12 req/day → 3 fetches)
|
||||
lat: <latitude> # decimal degrees, e.g. 48.7839
|
||||
lon: <longitude> # decimal degrees, e.g. 9.3889
|
||||
declination: <tilt> # panel tilt in degrees from horizontal (0=flat, 90=vertical)
|
||||
azimuth: <azimuth> # degrees from south (south=0, west=90, east=-90)
|
||||
kwp: <kwp> # installed peak power in kWp, e.g. 8.5
|
||||
fetch_interval: "4h" # re-fetch during the day (free tier: 12 req/day → 3 fetches)
|
||||
fetch_window_start: "07:00"
|
||||
fetch_window_end: "19:00"
|
||||
base_load_w: 450 # steady-state house consumption (W) — spikes from kettle/dishwasher are hourly averages and negligible
|
||||
min_surplus_w: 1800 # min export surplus to trigger wallbox — matches wallbox_a threshold
|
||||
base_load_w: 400 # steady-state house consumption (W) — measure and adjust
|
||||
min_surplus_w: 1800 # min export surplus to trigger wallbox charging (W)
|
||||
|
||||
# EMS operational settings
|
||||
ems:
|
||||
|
||||
15
deploy/son/.env
Normal file
15
deploy/son/.env
Normal file
@@ -0,0 +1,15 @@
|
||||
# ── Son's EMS stack — edit these values before first start ──────────────────
|
||||
|
||||
# Static IP of this Synology on the LAN (no trailing slash)
|
||||
SYNOLOGY_IP=192.168.1.100
|
||||
|
||||
# Fronius DataManager IP (the box with the display / web interface)
|
||||
FRONIUS_IP=192.168.1.XXX
|
||||
|
||||
# Elli Charger Connect 2 — OCPP station IDs
|
||||
# Find these in the Elli app under Settings → Charging Station → Station ID
|
||||
ELLI_A_STATION_ID=ELLI-XXXXXXXXXX-1
|
||||
ELLI_B_STATION_ID=ELLI-XXXXXXXXXX-2
|
||||
|
||||
# Timezone
|
||||
TZ=Europe/Berlin
|
||||
101
deploy/son/README.md
Normal file
101
deploy/son/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# EMS Stack — Einrichtung auf der Synology
|
||||
|
||||
## Was läuft hier?
|
||||
|
||||
| Dienst | Adresse | Funktion |
|
||||
|---|---|---|
|
||||
| evcc | `http://SYNOLOGY-IP:7070` | Ladesteuerung, PV-Überschuss, Haupt-UI |
|
||||
| Grafana | `http://SYNOLOGY-IP:3000` | Historische Auswertungen |
|
||||
| Prometheus | `http://SYNOLOGY-IP:9090` | Metrik-Speicher (intern) |
|
||||
|
||||
**Passwort Grafana:** `ems2024` (Benutzer: `admin`)
|
||||
|
||||
---
|
||||
|
||||
## Einmalige Einrichtung
|
||||
|
||||
### 1. Synology vorbereiten
|
||||
|
||||
Im Synology **Package Center**: **Container Manager** installieren (kostenlos).
|
||||
|
||||
### 2. Dateien auf die Synology kopieren
|
||||
|
||||
Den gesamten Ordner `deploy/son/` per FileStation oder SCP auf die Synology kopieren,
|
||||
z.B. nach `/volume1/docker/ems/`.
|
||||
|
||||
### 3. `.env` anpassen
|
||||
|
||||
Die Datei `.env` im Texteditor öffnen und ausfüllen:
|
||||
|
||||
```
|
||||
SYNOLOGY_IP=192.168.1.100 ← IP-Adresse der Synology
|
||||
FRONIUS_IP=192.168.1.XXX ← IP-Adresse des Fronius DataManagers
|
||||
ELLI_A_STATION_ID=ELLI-... ← Station-ID aus der Elli-App (Wallbox A)
|
||||
ELLI_B_STATION_ID=ELLI-... ← Station-ID aus der Elli-App (Wallbox B)
|
||||
```
|
||||
|
||||
**Station-ID in der Elli-App:** Einstellungen → Ladestation → Station-ID
|
||||
|
||||
### 4. Elli Wallboxes konfigurieren
|
||||
|
||||
In der **Elli-App** für jede Wallbox:
|
||||
- Einstellungen → Backend / OCPP
|
||||
- Backend-URL: `ws://SYNOLOGY-IP:8887/ocpp`
|
||||
- Station-ID: wie in `.env` eingetragen
|
||||
|
||||
### 5. Stack starten
|
||||
|
||||
Im Synology **Container Manager** → **Projekt** → **Erstellen**:
|
||||
- Projektname: `ems`
|
||||
- Pfad: Ordner mit der `docker-compose.yaml`
|
||||
- Starten
|
||||
|
||||
Oder per SSH:
|
||||
```bash
|
||||
cd /volume1/docker/ems
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 6. Prüfen ob alles läuft
|
||||
|
||||
- evcc öffnen: `http://SYNOLOGY-IP:7070`
|
||||
- Unter **Geräte** sollten Fronius (PV + Batterie + Netz) und beide Wallboxen grün erscheinen
|
||||
- Grafana: `http://SYNOLOGY-IP:3000` → Dashboard **evcc — Laden & PV**
|
||||
|
||||
---
|
||||
|
||||
## Wenn ein Fronius-Wechselrichter eine eigene IP hat
|
||||
|
||||
Falls doch zwei getrennte DataManager (zwei IP-Adressen):
|
||||
In `evcc.yaml` den Abschnitt `meters` → `pv` auf zwei Einträge aufteilen:
|
||||
|
||||
```yaml
|
||||
- name: pv1
|
||||
type: template
|
||||
template: fronius-solarapi-v1
|
||||
host: 192.168.1.XXX # Wechselrichter 1 (11 kW)
|
||||
usage: pv
|
||||
|
||||
- name: pv2
|
||||
type: template
|
||||
template: fronius-solarapi-v1
|
||||
host: 192.168.1.YYY # Wechselrichter 2 (22 kW)
|
||||
usage: pv
|
||||
```
|
||||
|
||||
Und in `site.meters.pv` beide eintragen:
|
||||
```yaml
|
||||
pv:
|
||||
- pv1
|
||||
- pv2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updates
|
||||
|
||||
```bash
|
||||
cd /volume1/docker/ems
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
64
deploy/son/docker-compose.yaml
Normal file
64
deploy/son/docker-compose.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
services:
|
||||
|
||||
# ── evcc — EV charging controller ─────────────────────────────────────────
|
||||
evcc:
|
||||
image: evcc/evcc:latest
|
||||
container_name: evcc
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7070:7070" # evcc web UI
|
||||
- "8887:8887" # OCPP server (Elli wallboxes connect here)
|
||||
volumes:
|
||||
- ./evcc.yaml:/etc/evcc.yaml:ro
|
||||
- evcc-data:/root/.evcc
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
networks:
|
||||
- ems-net
|
||||
|
||||
# ── Prometheus — metrics storage ───────────────────────────────────────────
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: prometheus
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yaml:/etc/prometheus/prometheus.yml:ro
|
||||
- prometheus-data:/prometheus
|
||||
command:
|
||||
- --config.file=/etc/prometheus/prometheus.yml
|
||||
- --storage.tsdb.retention.time=90d
|
||||
- --storage.tsdb.path=/prometheus
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
networks:
|
||||
- ems-net
|
||||
|
||||
# ── Grafana — dashboards ───────────────────────────────────────────────────
|
||||
grafana:
|
||||
image: grafana/grafana-oss:latest
|
||||
container_name: grafana
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./grafana/provisioning:/etc/grafana/provisioning:ro
|
||||
- grafana-data:/var/lib/grafana
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=ems2024
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
- GF_ANALYTICS_REPORTING_ENABLED=false
|
||||
- GF_SERVER_ROOT_URL=http://${SYNOLOGY_IP}:3000
|
||||
networks:
|
||||
- ems-net
|
||||
|
||||
volumes:
|
||||
evcc-data:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
|
||||
networks:
|
||||
ems-net:
|
||||
driver: bridge
|
||||
94
deploy/son/evcc.yaml
Normal file
94
deploy/son/evcc.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
# evcc configuration — son's installation
|
||||
# Edit the IPs/IDs below to match your hardware.
|
||||
# All values with XXX must be filled in before starting.
|
||||
|
||||
network:
|
||||
schema: http
|
||||
host: 0.0.0.0 # listen on all interfaces inside container
|
||||
port: 7070
|
||||
|
||||
log: warn
|
||||
levels:
|
||||
core: info
|
||||
charger: info
|
||||
|
||||
# ── Interval ─────────────────────────────────────────────────────────────────
|
||||
interval: 30s # control loop interval
|
||||
|
||||
# ── Grid meter (Fronius Smart Meter via DataManager) ─────────────────────────
|
||||
meters:
|
||||
- name: grid
|
||||
type: template
|
||||
template: fronius-solarapi-v1
|
||||
host: ${FRONIUS_IP} # Fronius DataManager IP
|
||||
usage: grid
|
||||
|
||||
- name: pv
|
||||
type: template
|
||||
template: fronius-solarapi-v1
|
||||
host: ${FRONIUS_IP} # same DataManager — covers both inverters aggregated
|
||||
usage: pv
|
||||
|
||||
- name: battery
|
||||
type: template
|
||||
template: fronius-solarapi-v1
|
||||
host: ${FRONIUS_IP} # BYD battery connected to Fronius
|
||||
usage: battery
|
||||
|
||||
# ── Site ─────────────────────────────────────────────────────────────────────
|
||||
site:
|
||||
title: "Haus"
|
||||
meters:
|
||||
grid: grid
|
||||
pv:
|
||||
- pv
|
||||
battery:
|
||||
- battery
|
||||
|
||||
# ── Chargers (Elli Charger Connect 2 via OCPP) ───────────────────────────────
|
||||
# The Elli app: Settings → Charging Station → Backend URL
|
||||
# Set to: ws://<SYNOLOGY_IP>:8887/ocpp
|
||||
# The station ID is shown in the same screen.
|
||||
chargers:
|
||||
- name: wallbox_a
|
||||
type: template
|
||||
template: ocpp
|
||||
stationid: ${ELLI_A_STATION_ID}
|
||||
connecttimeout: 5m
|
||||
|
||||
- name: wallbox_b
|
||||
type: template
|
||||
template: ocpp
|
||||
stationid: ${ELLI_B_STATION_ID}
|
||||
connecttimeout: 5m
|
||||
|
||||
# ── Load points ──────────────────────────────────────────────────────────────
|
||||
loadpoints:
|
||||
- title: "Wallbox A (11 kW)"
|
||||
charger: wallbox_a
|
||||
mode: pv # default: charge on PV surplus only
|
||||
mincurrent: 6 # A — minimum OCPP current (legal minimum = 6A)
|
||||
maxcurrent: 16 # A — 16A × 3 phases × 230V ≈ 11 kW
|
||||
phases: 3
|
||||
|
||||
- title: "Wallbox B (8 kW)"
|
||||
charger: wallbox_b
|
||||
mode: pv
|
||||
mincurrent: 6
|
||||
maxcurrent: 12 # A — 12A × 3 phases × 230V ≈ 8.3 kW
|
||||
phases: 3
|
||||
|
||||
# ── Tariff (optional — enables cost display) ─────────────────────────────────
|
||||
# Uncomment and adjust if you want cost/CO2 tracking
|
||||
# tariffs:
|
||||
# currency: EUR
|
||||
# grid:
|
||||
# type: fixed
|
||||
# price: 0.30 # €/kWh
|
||||
|
||||
# ── OCPP server ───────────────────────────────────────────────────────────────
|
||||
ocpp:
|
||||
port: 8887
|
||||
|
||||
# ── Prometheus metrics ────────────────────────────────────────────────────────
|
||||
# evcc exposes /metrics on the main port (7070)
|
||||
10
deploy/son/grafana/provisioning/dashboards/dashboard.yaml
Normal file
10
deploy/son/grafana/provisioning/dashboards/dashboard.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: evcc
|
||||
type: file
|
||||
disableDeletion: false
|
||||
updateIntervalSeconds: 60
|
||||
options:
|
||||
path: /etc/grafana/provisioning/dashboards
|
||||
foldersFromFilesStructure: false
|
||||
1
deploy/son/grafana/provisioning/dashboards/evcc.json
Normal file
1
deploy/son/grafana/provisioning/dashboards/evcc.json
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
9
deploy/son/prometheus/prometheus.yaml
Normal file
9
deploy/son/prometheus/prometheus.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: evcc
|
||||
static_configs:
|
||||
- targets: ['evcc:7070']
|
||||
metrics_path: /metrics
|
||||
Reference in New Issue
Block a user