Commit Graph

9 Commits

Author SHA1 Message Date
fc535d395c Add per-consumer accepted import tolerance for wallboxes
With a 4.6kW inverter and 4kW WallboxB + ~450W house load, any PV dip
causes brief grid import and would trigger shutdown/restart cycling.

New config: wallbox_a_accepted_import_w / wallbox_b_accepted_import_w
WallboxB set to 600W: engine skips shutdown if import <= 600W, keeping
the car charging through short cloud shadows. WallboxA disabled (2kW
leaves sufficient inverter headroom without needing tolerance).

Logic: shutdownLastConsumer checks per-consumer tolerance before
applying min-runtime and initiating turn-off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:34:18 +02:00
afbb7bf62c Add WW boost enable/disable toggle (persistent flag file)
In summer the WW boost interferes with car charging priority.
A new / toggle on the WW consumer card persists to
/etc/ems/ww-boost-off (presence = disabled, absence = enabled).

- config: ww_boost_disable_file flag file path
- main: wwBoostDisabled mode, wwBoostToggleHandler, runCycle skips
  computeWWBoost when disabled
- status: WWBoostDisabled in pageData, toggle button in WW card
  showing  Boost aus /  Boost ein depending on state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 16:09:33 +02:00
406046c3a9 Add trip mode: self-learning EV charging scheduler
User enters car SOC + departure deadline; EMS computes when to start
charging and activates the wallbox at the right time (grid import OK
for trip mode). Charging rate is learned from completed PM sessions,
improving over time. Car profiles (Mini Cooper SE 50 kWh, BMW ix2 63
kWh) are config-driven and selectable per trip.

- internal/trip/trip.go: Goal/Session types, Manager with per-cycle
  Tick() that accumulates energy, detects session completion, learns
  avg charge rate from JSONL session log
- internal/config: CarProfile + Cars map, TripGoalFile, SessionLogFile
- internal/status: trip card (active goal) + trip form (set new goal),
  CarOption list, formatDay/formatDur helpers
- main.go: tripMgr lifecycle, /trip + /trip/cancel HTTP handlers,
  runCycle integration (ShouldStartNow → ApplyOverride, auto-clear)
- configs/ems-config.yaml: cars section, trip_goal_file, session_log_file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 12:51:41 +02:00
00f8f3cdde Add monitor-only mode: suppress all actions without stopping EMS
New internal/monitor package provides a thread-safe, file-backed toggle.
Flag file presence (/etc/ems/monitor-only) = monitor-only active, survives
reboots. Deletion = resume normal operation.

Behaviour when active:
- EMS continues polling Prometheus, running the decision engine, and
  updating the status page every 2 minutes — full visibility maintained
- All actuator calls suppressed (Shelly switches + Viessmann WW writes)
- Suppressed actions logged as [MONITOR-ONLY] for audit trail
- Startup warns if flag file is already present

Web UI:
- Amber banner "⏸ Monitor-Only — Keine Schaltvorgänge" with inline
  "▶ Automatik" resume button when active
- Small unobtrusive "⏸ Monitor-Only" button at page bottom when inactive
- POST /monitor endpoint toggles state and redirects back to status page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:27:53 +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
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
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
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