Skip to content

Commit ddaf143

Browse files
authored
Merge pull request #241 from renaudallard/main
Preserve sub-integer precision during driving re-anchor
2 parents 046576f + f71f41d commit ddaf143

1 file changed

Lines changed: 28 additions & 6 deletions

File tree

custom_components/cardata/magic_soc.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,35 @@ def reanchor_driving_session(self, vin: str, new_soc: float, current_mileage: fl
379379
if session.anchor_soc == new_soc and session.anchor_mileage == current_mileage:
380380
return
381381
old_anchor = session.anchor_soc
382-
session.anchor_soc = new_soc
382+
# BMW sends integer SOC. If our sub-integer prediction rounds to that
383+
# integer (abs < 0.5), keep prediction as anchor to avoid cosmetic jumps.
384+
# Otherwise BMW disagrees and we correct to their value.
385+
#
386+
# P=pred N=bmw |diff| branch anchor display
387+
# 54.7 55 0.3 keep 54.7 54.7 (rounding, smooth)
388+
# 54.1 54 0.1 keep 54.1 54.1 (rounding, smooth)
389+
# 54.0 55 1.0 correct 55 55.0 (real drift up)
390+
# 54.0 54 0.0 keep 54.0 54.0 (exact match)
391+
# 54.7 54 0.7 correct 54 54.0 (real drift down)
392+
# 54.7 57 2.3 correct 57 57.0 (real drift up)
393+
# 54.4 54 0.4 keep 54.4 54.4 (rounding, smooth)
394+
# 54.4 55 0.6 correct 55 55.0 (54.4 != round(55))
395+
if abs(new_soc - session.last_predicted_soc) < 0.5:
396+
session.anchor_soc = session.last_predicted_soc
397+
else:
398+
session.anchor_soc = new_soc
399+
session.last_predicted_soc = new_soc
383400
session.anchor_mileage = current_mileage
384-
session.last_predicted_soc = new_soc
385401
# Transfer segment aux to trip total before resetting for new segment
386402
session.trip_total_aux_kwh += session.accumulated_aux_kwh
387403
session.accumulated_aux_kwh = 0.0
388404
_LOGGER.debug(
389-
"Magic SOC: Re-anchored %s from %.1f%% to %.1f%% at %.1f km",
405+
"Magic SOC: Re-anchored %s %.1f%% %.1f%% at %.1f km (BMW: %d%%)",
390406
redact_vin(vin),
391407
old_anchor,
392-
new_soc,
408+
session.anchor_soc,
393409
current_mileage,
410+
new_soc,
394411
)
395412

396413
def end_driving_session(self, vin: str, end_soc: float | None, end_mileage: float | None) -> None:
@@ -588,14 +605,19 @@ def get_magic_soc(self, vin: str, bmw_soc: float | None, mileage: float | None)
588605
if last_driving is not None:
589606
predicted_soc, saved_at = last_driving
590607
if (time.time() - saved_at) < DRIVING_SOC_CONTINUITY_SECONDS:
591-
# Use last prediction if BMW SOC appears stale (higher or equal)
592-
if bmw_soc is None or bmw_soc >= predicted_soc:
608+
# Use last prediction if BMW SOC is stale (higher/equal) or
609+
# within rounding of our sub-integer prediction
610+
if bmw_soc is None or bmw_soc >= predicted_soc or abs(bmw_soc - predicted_soc) < 0.5:
593611
self._last_magic_soc[vin] = predicted_soc
594612
return predicted_soc
595613
# Expired or BMW sent fresh lower SOC — discard
596614
del self._last_driving_predicted_soc[vin]
597615

598616
if bmw_soc is not None:
617+
# Keep existing sub-integer prediction if BMW agrees within rounding
618+
existing = self._last_magic_soc.get(vin)
619+
if existing is not None and abs(bmw_soc - existing) < 0.5:
620+
return existing
599621
self._last_magic_soc[vin] = bmw_soc
600622
return bmw_soc
601623
return self._last_magic_soc.get(vin)

0 commit comments

Comments
 (0)