Files
EMS/CLAUDE.md
Lutz Finsterle 99613c52ae Initial commit: EMS — Energie Management System
Complete self-consumption optimisation system for 7 kWp PV installation:
- Prometheus collector (grid power, SOC, PV, per-phase, compressor)
- Pure decision engine with SOC gates, hysteresis, priority ordering
- Shelly Gen1/Gen2 actuator (SHA-256 Digest auth, PM power readback)
- Viessmann OAuth2 client for DHW temperature control
- PV forecast integration (forecast.solar)
- Wallbox mutual exclusion (VX3 4.6 kW AC output constraint)
- Car-not-charging detection via Shelly PM
- Compressor idle → early SG-Ready release
- Per-phase grid power for single-phase wallbox decisions
- Manual override detection and web UI with override buttons
- Full unit test coverage for decision engine
- systemd service, Makefile, complete documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:02:16 +02:00

77 lines
3.5 KiB
Markdown

# EMS — Energie Management System
## Project overview
Eigenverbrauchsoptimiertes Energie Management System für eine Heim-PV-Anlage.
Liest Zustandsdaten aus einem bestehenden Prometheus, trifft Schaltentscheidungen
basierend auf SOC-Schwellen und Hysterese, und steuert Shelly-Aktoren per HTTP.
## Architecture
```
Prometheus (192.168.0.23:9090) → Collector (PromQL) → Decision Engine → Actuator → Shelly HTTP
Prometheus Exporter (:9099)
```
### Layers
- **Collector** (`internal/collector/`): Reads system state via PromQL instant queries from existing Prometheus
- **Engine** (`internal/engine/`): Pure decision logic — SOC gates, hysteresis, priority ordering. No side effects, fully testable.
- **Actuator** (`internal/actuator/`): Executes switching via Shelly Gen1/Gen2 HTTP APIs
- **Metrics** (`internal/metrics/`): Exports EMS decisions as Prometheus metrics on :9099
### Key design decisions
- Data reads come from Prometheus (already scraped every 2min by custom Viessmann exporter)
- Data writes (Shelly switching, Viessmann API for WW) are done directly by the EMS
- `pcc_transfer_power_exchange_value`: positive = grid import, negative = grid export
- Decision engine is pure: `func Decide(state, now) → []Action` — easy to unit test
- All thresholds configurable via YAML (`configs/ems-config.yaml`)
## Hardware setup
| Component | Details |
|---|---|
| PV | 7 kWp, Trina VX3 4.6 hybrid inverter |
| Battery | 8 kWh, managed autonomously by VX3 |
| Heat pump | Viessmann Vitocal 200-S AWB-E-AC |
| SG-Ready | Shelly 1 Gen1 @ 192.168.42.90 → heating buffer +5°C |
| Wallbox A | 2kW, Shelly Plus 1 PM Gen2 @ 192.168.42.107 |
| Wallbox B | 4kW, Shelly Plus 1 Gen2 @ 192.168.42.51 |
| WW control | Viessmann API (OAuth2, refresh token) — NOT YET IMPLEMENTED |
## Prometheus metrics (read from existing instance)
- `pcc_transfer_power_exchange_value` — grid power (pos=import, neg=export)
- `ess_stateOfCharge_value` — battery SOC (0-100%)
- `photovoltaic_production_current_value` — current PV production (W)
- `ess_power_value` — battery charge/discharge (W)
- `heating_compressors_0_power_value` — heat pump compressor power (W)
- `heating_sensors_temperature_outside_value` — outdoor temp (°C)
## Decision logic summary
1. SOC gates: <50% all blocked, 50-70% SG-Ready only, 70-90% +WB_A, >90% all
2. Export threshold + hysteresis (4min on, 6min off)
3. Priority: SG-Ready (heating only) → WB_A (2kW) → WB_B (4kW)
4. Shutdown: reverse order, respecting min runtimes (15min WB, 30min SG-Ready)
5. Emergency brake: SOC drops below threshold → immediate shutdown
## TODO / open items
- [ ] Viessmann API OAuth2 write integration (WW-Solltemperatur)
- [ ] PV Forecast integration (Solcast/forecast.solar) for strategic layer
- [ ] Modbus TCP/RS485 for faster PCC data (Phase 3)
- [ ] Grafana dashboard for EMS metrics
- [ ] go.sum generation (`go mod tidy`)
- [ ] Integration tests with mock Prometheus / mock Shelly endpoints
## Commands
```bash
make build # Build binary
make test # Run unit tests
make dry-run # Run without switching (log only)
make run # Run with live switching
make install # Install as systemd service
```
## Code style
- Go 1.23+, structured logging via `log/slog`
- Idiomatic Go: no frameworks, standard library where possible
- Engine is pure (no I/O) — all side effects in Collector and Actuator
- Config via YAML, parsed with gopkg.in/yaml.v3
- Prometheus metrics via prometheus/client_golang