- WW card shows current tank temperature (Speicher: 48.7°C)
- SG-Ready card shows inline compressor power (Kompressor: 1.5 kW / Standby)
- Consumer reason text now wraps instead of being silently truncated
- Add readiness hints on inactive wallboxes: explains what's blocking
activation (SOC too low / forecast insufficient / waiting for PV ≥ Xw)
- Add WW readiness hint when forecast is below threshold
- Move Monitor-Only toggle button to header (next to timestamp) for
symmetric placement with the resume button in the monitor banner
- Remove charging advice card (outdated for proactive charging logic)
- Remove compressor power footnote from page bottom (now inline in card)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The reset and boost toggle buttons were side by side, looking cramped.
Wrapped them in a flex column div so they stack neatly below each other.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Template range loop sets . to consumerView, not pageData, so
.WWBoostDisabled was undefined. Added the field to consumerView
and populated it from the wwBoostDisabled local in the handler.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Compressor:
- metric changed to PromQL expression that returns actual watts:
sensors_power_value(%) × power_value(kW) × 10
e.g. 27% × 10 kW = 2700 W; idle = 0 W
- idle threshold raised 20→500 W (clean gap: 0W idle vs ≥2700W running)
- status page now displays real kW instead of percent
WW boost reset:
- Added 🌡️ Zurücksetzen button to WW consumer card (when configured)
- POST /ww/reset: restores WWBaseC via Viessmann API + blocks re-boost
via engine override until midnight
- IsWW field added to consumerView for template control
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>