@@ -80,6 +80,13 @@ def _descriptor_float(state: DescriptorState | None) -> float | None:
8080 return None
8181
8282
83+ def _has_ac_power_data (vehicle_state : dict [str , DescriptorState ]) -> bool :
84+ """Check if AC voltage × current data is available."""
85+ voltage = _descriptor_float (vehicle_state .get ("vehicle.drivetrain.electricEngine.charging.acVoltage" ))
86+ current = _descriptor_float (vehicle_state .get ("vehicle.drivetrain.electricEngine.charging.acAmpere" ))
87+ return bool (voltage and current )
88+
89+
8390@dataclass
8491class CardataCoordinator :
8592 hass : HomeAssistant
@@ -513,7 +520,7 @@ def _anchor_soc_session(self, vin: str, vehicle_state: dict[str, DescriptorState
513520 except (TypeError , ValueError ):
514521 pass
515522 else :
516- # AC: seed with voltage × current if available
523+ # AC: try voltage × current first, fall back to charging.power
517524 voltage = _descriptor_float (vehicle_state .get ("vehicle.drivetrain.electricEngine.charging.acVoltage" ))
518525 current = _descriptor_float (vehicle_state .get ("vehicle.drivetrain.electricEngine.charging.acAmpere" ))
519526 phases = _descriptor_float (vehicle_state .get ("vehicle.drivetrain.electricEngine.charging.phaseNumber" ))
@@ -526,6 +533,17 @@ def _anchor_soc_session(self, vin: str, vehicle_state: dict[str, DescriptorState
526533 pass
527534 if voltage and current :
528535 self ._soc_predictor .update_ac_charging_data (vin , voltage , current , phases , aux_kw )
536+ else :
537+ # Fallback: seed from charging.power when V×A is unavailable
538+ power_state = vehicle_state .get ("vehicle.powertrain.electric.battery.charging.power" )
539+ if power_state and power_state .value is not None :
540+ try :
541+ power_val = float (power_state .value )
542+ unit = (power_state .unit or "" ).lower ()
543+ power_kw = power_val / 1000.0 if unit == "w" else power_val
544+ self ._soc_predictor .update_power_reading (vin , power_kw , aux_power_kw = aux_kw )
545+ except (TypeError , ValueError ):
546+ pass
529547
530548 def _end_soc_session (self , vin : str , vehicle_state : dict [str , DescriptorState ]) -> None :
531549 """End SOC prediction session when charging stops.
@@ -983,7 +1001,7 @@ async def _async_handle_message_locked(
9831001 # Charging started - try to anchor session
9841002 self ._anchor_soc_session (vin , vehicle_state )
9851003 # End any active driving session (can't drive and charge)
986- self ._magic_soc . cancel_driving_session (vin )
1004+ self ._end_driving_session (vin , vehicle_state )
9871005 elif was_charging :
9881006 # Charging stopped - end session for learning
9891007 self ._end_soc_session (vin , vehicle_state )
@@ -1004,10 +1022,11 @@ async def _async_handle_message_locked(
10041022 if value :
10051023 self ._soc_predictor .set_charging_method (vin , str (value ))
10061024
1007- # Update power reading with direct power value (DC charging only;
1008- # AC charging uses voltage × amps instead )
1025+ # Update power reading with direct power value (DC charging,
1026+ # or AC fallback when voltage × amps data is unavailable )
10091027 elif descriptor == "vehicle.powertrain.electric.battery.charging.power" :
1010- if self ._soc_predictor .get_charging_method (vin ) == "DC" :
1028+ method = self ._soc_predictor .get_charging_method (vin )
1029+ if method == "DC" or (method is not None and not _has_ac_power_data (vehicle_state )):
10111030 power_kw = None
10121031 if value is not None :
10131032 try :
0 commit comments