Commit Graph

13 Commits

Author SHA1 Message Date
0cd481a5ea Add Docker Compose stack for son's installation (evcc + Prometheus + Grafana)
Self-contained stack for DS218 Synology targeting:
- 2x Fronius inverters (11+22 kW) via shared DataManager, Solar.API v1
- BYD 12 kWh battery connected through Fronius
- 2x Elli Charger Connect 2 via OCPP 1.6j
- evcc handles PV surplus charging, OCPP server built-in
- Prometheus scrapes evcc /metrics, 90d retention
- Grafana auto-provisioned with evcc dashboard and Prometheus datasource

Setup: edit .env (3 IPs + 2 station IDs), point Elli app to OCPP backend,
docker compose up -d. README in German for non-IT handover.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 15:53:44 +02:00
ca0f9ed1c3 house/son: placeholder config for son's installation
All house-specific values (IPs, coordinates, Viessmann credentials, kWp)
replaced with <...> placeholders. Ready to fill in once hardware is known.
Notable differences from house/lutz template:
- installation_id left empty (disables WW boost until credentials obtained)
- base_load_w set to 400W (adjust after measuring)
- forecast lat/lon/declination/azimuth/kwp all require local values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:19:30 +02:00
0b51ce5240 Add ambient temperature gate to heating period detection
Adds heating_min_ambient_c (default 15°C) to season config. When outdoor
temperature is at or above this threshold, SG-Ready is suppressed even if
the calendar month is within the heating season. Prevents unnecessary heat
pump boost activation on warm spring/autumn days.

Logic: heating active = in_heating_month AND ambient < threshold
Zero value (unset) disables the temperature gate (calendar-only, old behaviour).

New test: TestHeatingPeriodAmbientSuppression covers warm-day suppression,
cold-day pass-through, and summer month independence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:13:05 +02:00
cc33add507 Show consumer priority order on status page
Section title changed to "Verbraucher — Priorität ↓" and each consumer
row now shows a circled number (① ② ③ ④) so it's immediately clear
which consumer has precedence for turn-on and reverse order for shutdown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:08:34 +02:00
56710b4328 Fix Viessmann API hostname and harden test propagation waits
- Update apiBase: api.viessmann.com → api.viessmann-climatesolutions.com
  (Viessmann renamed their IoT API host; old domain is NXDOMAIN)
- Extend --test-viessmann context to 120s
- Add 30s propagation wait before read-back in both Stage 3 and Restore:
  cloud accepts writes immediately but gateway polls on its own schedule,
  so GET returns the last confirmed device value with ~30s lag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:05:33 +02:00
8ba86f8fc8 Add --test-viessmann flag for end-to-end API validation
Adds GetDHWTemperature() to the Viessmann client (GET on the DHW feature
endpoint) and a --test-viessmann CLI flag that runs three stages without
starting the EMS control loop:

  Stage 1: read current DHW setpoint (confirms auth + endpoint path)
  Stage 2: write same value back, read-back to confirm round-trip
  Stage 3: write +1°C, read-back to confirm, restore original

The +1°C delta is below the heat pump's 5°C hysteresis so the compressor
will not fire, but the change is visible in the Viessmann app for visual
confirmation before trusting the WW boost logic in production.

Usage: ./ems --config /etc/ems/ems-config.yaml --test-viessmann

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:59:57 +02:00
590ba1e675 docs: update README for all features added since initial commit
- Forecast config: fetch_interval, fetch_window_start/end, base_load_w,
  min_surplus_w — 3x daily refresh within daylight window
- EMS config: override_max_import_w — hard-stop override on excess import
- Decision logic: charging advice concept and surplus window algorithm
- Decision logic: override hard-stop conditions (SOC brake + import limit)
- Web interface: charging advice card, PM live power badges, phase display,
  compressor power, override duration selector (30m/1h/2h/4h)
- Makefile: note that make install skips config if file already exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.9
2026-04-06 10:51:22 +02:00
7a971c4c41 Add configurable override duration and hard-stop thresholds
Override duration (web UI):
- Dropdown on Ein button: 30min / 1h / 2h / 4h
- Engine notified immediately via ApplyOverride() — no waiting for
  next SyncHardwareState cycle
- Aus button always uses 1h lockout (keeps consumer off for 1h)

Hard-stop thresholds that cancel active overrides:
- SOC emergency brake: now also clears ManualOverride flag so EMS
  resumes full control after the safety shutdown
- override_max_import_w (default 800W): if grid import exceeds this
  while an override is active, override is cancelled immediately
  (no hysteresis delay — protection is instant)

Config: ems.override_max_import_w (0 = disabled)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:43:49 +02:00
4530b672d2 Tune base_load_w to 450W (measured steady-state house consumption)
Kettle/dishwasher spikes are seconds-long against hourly forecast
averages — negligible. Correct value is the steady-state draw.

Effect: surplus window today widens from 11:00-13:00 to 10:00-17:00.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:33:53 +02:00
0020f1268b Add EV charging advice notification to status page
Uses hourly PV forecast data (watts per hour from forecast.solar) to
find the first contiguous block of hours where:
  PV production - base_load_w >= min_surplus_w

Shows a blue advice card on the status page:
  "Auto einstecken bis HH:MM Uhr"
  "Erwartetes Überschuss-Fenster: HH:MM – HH:MM Uhr"

Card is hidden when:
- No surplus window found (weak forecast day)
- Window has already started or passed
- A wallbox is currently active (car already charging)

Config: forecast.base_load_w (1000W), forecast.min_surplus_w (1800W)
Timestamp parsing handles both "HH:MM:SS" and "HH:MM" key formats.
Today: surplus window 11:00–13:00 (24.3 kWh forecast).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:30:58 +02:00
5f22df3e0f Refresh forecast up to 3× per day within daylight window
forecast.solar updates estimates as the day progresses, so a single
morning fetch can be stale by afternoon. New behaviour:
- Re-fetch every 4h within 07:00–19:00 window (3 fetches/day)
- Outside the window always serve the cached value
- Free tier limit is 12 req/day — 3 fetches is well within budget
- First startup outside window still fetches if cache is empty

Config: forecast.fetch_interval, fetch_window_start, fetch_window_end

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:19:29 +02:00
79828a46c5 Add status page enhancements and engine improvements
Status page:
- Per-phase grid power (L1/L2/L3) with export/import direction
- Shelly PM live power badge on active wallbox rows
- Compressor power shown below consumer list
- SyncDeviceStates() method on Store for PM power readback

Engine:
- Fix wallbox mutex: re-read Active state after P3 so same-cycle
  WallboxA activation correctly blocks WallboxB in P4
- Fix RecoverState: set ActivatedAt to zero so recovered consumers
  are immediately eligible for shutdown decisions
- Add 3 new unit tests: mutual exclusion, car-not-charging,
  compressor-idle early release

Makefile:
- make install no longer overwrites /etc/ems/ems-config.yaml if it
  already exists (prevents password loss on updates)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:06:01 +02:00
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