Skip to content

v0.4.0 — Session quality fixes (post-PR-22 stabilization)

Choose a tag to compare

@cryptotomte cryptotomte released this 24 May 06:02
bc55d71

Stabilization release on top of v0.3.0's plug-anchored session model

Six bundled fixes surfaced during real-world validation of PR-22 (three charging cycles across two vehicles + one HA Core upgrade mid-session). All fixes apply additively to the existing PlugAnchoredSessionEngine; no session-store schema change, no migration required.

What's new

  • RFID grace timer — plug-then-blip now produces correctly attributed sessions. After plug-on, the engine waits up to 5 seconds for an RFID blip before committing user attribution. Existing blip-then-plug fast path is unchanged. The previous race condition (plug-on commits user=Unknown 12 ms before the RFID blip propagates from the charger) is closed.
  • HA-restart preserves charging duration — when HA restarts mid-session, the pre-restart open charging window is now reconstructed as a synthetic closed window during deferred recovery, with ended_at = recovery time and energy_kwh covering the full pre-restart delta. charging_duration_s and avg_power_w are now physically plausible across restarts (previously: a Peugeot session reported 45 kW average for a 3.7 kW PHEV).
  • Window counter unified across log linesCHARGING_WINDOW_OPEN and CHARGING_WINDOW_CLOSE now both use session.charging_window_count (the session-wide persisted counter). Previously, after a restart, a window could open as window=2 and close as window=1 because the engine-instance counter resets on restart.
  • EV Charger Charging binary sensor honest about current charging — flips to off when the active charging window closes (sub-state charged), instead of staying on for the entire plug duration. Generic profile (legacy SessionEngine) keeps its previous TRACKING-based behavior via automatic fallback.
  • New live Charging duration sensor (sv: Laddningstid) — accumulating across charging windows, with per-window detail in extra_state_attributes (index, started_at, ended_at, duration_s, energy_kwh). Distinct from Session duration (anslutningstid). The two diverge significantly when the car sits charged on the cable (e.g. 14:21:01 connection vs 04:45:50 charging on a typical overnight Mercedes session).
  • HEARTBEAT log + live UI dispatch — debug log now writes a HEARTBEAT line at a configurable cadence (default 5 min) while a session is being tracked, so users can verify the engine is alive during long stable charges. A separate UI dispatch timer (default 60 s) keeps the live duration sensors ticking smoothly in Lovelace between state changes.

Configuration

Three new options under integration settings:

  • rfid_grace_seconds (default 5, range 0–30 where 0 disables the grace timer)
  • heartbeat_log_interval_min (default 5, range 0–30 where 0 disables HEARTBEAT logging)
  • ui_dispatch_interval_s (default 60, value 0 disables; otherwise range 10–300)

Internal improvements

  • _RfidGraceState dataclass replaces three scattered fields (cleaner lifecycle).
  • SessionSubState StrEnum replaces magic-string sub-state comparisons.
  • _Unsub = CALLBACK_TYPE | None alias replaces Any on ~9 timer/listener handles.
  • _safe_cancel(attr) helper deduplicates 4 cancel patterns.
  • _WindowAttribute / _ChargingDurationAttributes TypedDicts type the new sensor's attribute shape.
  • Idempotency guard on _async_start_session aborts late tasks after plug-off (FR-004).
  • connection_s clamped to max(0, ...) at all sites (protects against clock skew making real sessions look like micro-sessions).
  • HEARTBEAT line uses explicit is None checks for energy/power readings (no truthy-or masking of genuine 0.0 readings between windows).

Validation

Verified against three real-world charging cycles in May 2026:

  • Mercedes GLB 350 ("Elvis"): 48 kWh overnight + 9.5 h "Färdigladdad" idle on cable — clean.
  • Peugeot 3008 PHEV ("Petras bil") Day 1: 10.9 kWh, RFID race reproduced and now fixed.
  • Peugeot 3008 PHEV Day 2: 8.2 kWh with HA Core upgrade mid-session — multi-window recovery path exercised in production.

535 unit tests pass (478 baseline + 57 new for v0.4.0). ruff format and ruff check clean.

Migration

None required. Session store stays at schema v1.2; no field renames; no data backfill. New options use sensible defaults — existing installations get the new behavior automatically. The previous immediate-start RFID behavior remains available by setting rfid_grace_seconds = 0.

Scope

This release affects only the goe_gemini charger profile. The generic profile keeps the legacy SessionEngine unchanged.

Known follow-up

When pre-restart sub-state was charged (charging completed, cable still in) and HA restarts followed by unplug without resuming charging, charging_duration_s becomes 0 because the window-tracker doesn't reconstruct closed windows from the persisted session.windows[] snapshot. The synthetic-window guard correctly prevents the previous inflation bug, but full preservation requires loading persisted windows into the tracker at recovery — tracked for a future release.