diff --git a/src/handlers/message.py b/src/handlers/message.py index 9cb8a1d..7ae1e56 100644 --- a/src/handlers/message.py +++ b/src/handlers/message.py @@ -151,12 +151,10 @@ def __should_poll(self) -> bool: if len(refresh_modes) == 0 or all( mode == RefreshMode.OFF for mode in refresh_modes ): - logging.debug( - "Not checking for new messages as all cars have RefreshMode.OFF" - ) + LOG.debug("Not checking for new messages as all cars have RefreshMode.OFF") return False if self.relogin_handler.relogin_in_progress: - logging.warning( + LOG.warning( "Not checking for new messages as we are waiting to log back in" ) return False diff --git a/src/handlers/relogin.py b/src/handlers/relogin.py index fdd2c4b..94f832e 100644 --- a/src/handlers/relogin.py +++ b/src/handlers/relogin.py @@ -27,7 +27,7 @@ def relogin_in_progress(self) -> bool: def relogin(self) -> None: if self.__login_task is None: - logging.warning( + LOG.warning( f"API Client got logged out, logging back in {self.__relogin_relay} seconds" ) self.__login_task = self.__scheduler.add_job( @@ -45,9 +45,7 @@ async def login(self) -> None: login_response_message = await self.__api.login() LOG.info("Logged in as %s", login_response_message.account) except Exception as e: - logging.exception( - "Could not login to the SAIC API due to an error", exc_info=e - ) + LOG.exception("Could not login to the SAIC API due to an error", exc_info=e) raise e finally: if self.__scheduler.get_job(JOB_ID) is not None: diff --git a/src/handlers/vehicle.py b/src/handlers/vehicle.py index 487ff71..c506be7 100644 --- a/src/handlers/vehicle.py +++ b/src/handlers/vehicle.py @@ -150,6 +150,8 @@ async def __polling(self) -> None: vehicle_status_processing_result, ) = await self.update_vehicle_status() + charge_status = None + charge_status_processing_result = None if self.vin_info.is_ev: try: ( @@ -158,8 +160,6 @@ async def __polling(self) -> None: ) = await self.update_charge_status() except Exception as e: LOG.exception("Error updating charge status", exc_info=e) - charge_status = None - charge_status_processing_result = None try: await self.update_scheduled_battery_heating_status() @@ -477,7 +477,7 @@ async def handle_mqtt_command(self, *, topic: str, payload: str) -> None: msg = f"Error setting value for payload {payload}" raise MqttGatewayException(msg) from e else: - logging.info( + LOG.info( "Unknown Target SOC: waiting for state update before changing charge current limit" ) msg = f"Error setting charge current limit - SOC {self.vehicle_state.target_soc}" diff --git a/src/main.py b/src/main.py index 976a12d..3f114e3 100644 --- a/src/main.py +++ b/src/main.py @@ -16,7 +16,7 @@ # Enable fault handler to get a thread dump on SIGQUIT faulthandler.enable(file=sys.stderr, all_threads=True) - if hasattr(faulthandler, "register"): + if hasattr(faulthandler, "register") and hasattr(signal, "SIGQUIT"): faulthandler.register(signal.SIGQUIT, chain=False) configuration = process_arguments() diff --git a/src/publisher/mqtt_publisher.py b/src/publisher/mqtt_publisher.py index 98c0e7e..eb0af79 100644 --- a/src/publisher/mqtt_publisher.py +++ b/src/publisher/mqtt_publisher.py @@ -173,7 +173,7 @@ def __publish(self, topic: str, payload: Any) -> None: @override def is_connected(self) -> bool: - return cast(bool, self.client.is_connected) + return cast("bool", self.client.is_connected) @override def publish_json( diff --git a/src/status_publisher/charge/rvs_charge_status.py b/src/status_publisher/charge/rvs_charge_status.py index c1f11e5..a103301 100644 --- a/src/status_publisher/charge/rvs_charge_status.py +++ b/src/status_publisher/charge/rvs_charge_status.py @@ -114,40 +114,40 @@ def on_rvs_charge_status( def get_actual_battery_capacity( self, charge_status: RvsChargeStatus ) -> tuple[float, float]: - real_total_battery_capacity = self._vehicle_info.battery_capacity - if real_total_battery_capacity is not None and real_total_battery_capacity <= 0: + real_battery_capacity = self._vehicle_info.real_battery_capacity + if real_battery_capacity is not None and real_battery_capacity <= 0: # Negative or 0 value for real capacity means we don't know that info - real_total_battery_capacity = None + real_battery_capacity = None - raw_total_battery_capacity = None + raw_battery_capacity = None if ( charge_status.totalBatteryCapacity is not None and charge_status.totalBatteryCapacity > 0 ): - raw_total_battery_capacity = charge_status.totalBatteryCapacity / 10.0 + raw_battery_capacity = charge_status.totalBatteryCapacity / 10.0 - if raw_total_battery_capacity is not None: - if real_total_battery_capacity is not None: + if raw_battery_capacity is not None: + if real_battery_capacity is not None: LOG.debug( "Calculating full battery capacity correction factor based on " "real=%f and raw=%f", - real_total_battery_capacity, - raw_total_battery_capacity, + real_battery_capacity, + raw_battery_capacity, ) return ( - real_total_battery_capacity, - real_total_battery_capacity / raw_total_battery_capacity, + real_battery_capacity, + real_battery_capacity / raw_battery_capacity, ) LOG.debug( "Setting real battery capacity to raw battery capacity %f", - raw_total_battery_capacity, + raw_battery_capacity, ) - return raw_total_battery_capacity, 1.0 - if real_total_battery_capacity is not None: + return raw_battery_capacity, 1.0 + if real_battery_capacity is not None: LOG.debug( "Setting raw battery capacity to real battery capacity %f", - real_total_battery_capacity, + real_battery_capacity, ) - return real_total_battery_capacity, 1.0 + return real_battery_capacity, 1.0 LOG.warning("No battery capacity information available") return 0, 1.0 diff --git a/src/vehicle.py b/src/vehicle.py index 17138e3..c6c4bc2 100644 --- a/src/vehicle.py +++ b/src/vehicle.py @@ -612,8 +612,10 @@ def update_data_conflicting_in_vehicle_and_bms( soc_published = False if charge_status is not None: - if (range := charge_status.raw_fuel_range_elec) is not None: - electric_range_published = self.__publish_electric_range(range) + if (raw_fuel_range_elec := charge_status.raw_fuel_range_elec) is not None: + electric_range_published = self.__publish_electric_range( + raw_fuel_range_elec + ) if (soc := charge_status.raw_soc) is not None: soc_published = self.__publish_soc(soc / 10.0) @@ -626,10 +628,10 @@ def update_data_conflicting_in_vehicle_and_bms( soc_published = self.__publish_soc(vehicle_status.raw_soc) if not electric_range_published: - logging.warning("Could not extract a valid electric range") + LOG.warning("Could not extract a valid electric range") if not soc_published: - logging.warning("Could not extract a valid SoC") + LOG.warning("Could not extract a valid SoC") def handle_scheduled_battery_heating_status( self, scheduled_battery_heating_status: ScheduledBatteryHeatingResp | None diff --git a/src/vehicle_info.py b/src/vehicle_info.py index 7025e84..8cb7844 100644 --- a/src/vehicle_info.py +++ b/src/vehicle_info.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Final from exceptions import MqttGatewayException @@ -7,6 +8,8 @@ if TYPE_CHECKING: from saic_ismart_client_ng.api.vehicle import VehicleModelConfiguration, VinInfo +LOG = logging.getLogger(__name__) + class VehicleInfo: def __init__( @@ -106,12 +109,26 @@ def supports_target_soc(self) -> bool: return self.__get_property_value("Battery") == "1" @property - def battery_capacity(self) -> float | None: + def real_battery_capacity(self) -> float | None: if ( self.__custom_battery_capacity is not None and self.__custom_battery_capacity > 0 ): return float(self.__custom_battery_capacity) + if self.series.startswith("EH32"): + return self.__mg4_real_battery_capacity() + if self.series.startswith("EP2"): + return self.__mg5_real_battery_capacity() + # Model: MG ZS EV 2021 + if self.series.startswith("ZS EV"): + return self.__zs_ev_real_battery_capacity() + LOG.warning( + f"Unknown battery capacity for car series='{self.series}' and model='{self.model}'. " + "Please file an issue to improve data accuracy" + ) + return None + + def __mg4_real_battery_capacity(self) -> float | None: # MG4 high trim level if self.series.startswith("EH32 S"): if self.model.startswith("EH32 X3"): @@ -130,13 +147,20 @@ def battery_capacity(self) -> float | None: return 64.0 # MG4 low trim level with LFP battery return 51.0 + return None + + def __mg5_real_battery_capacity(self) -> float | None: # Model: MG5 Electric, variant MG5 SR Comfort if self.series.startswith("EP2CP3"): return 50.3 # Model: MG5 Electric, variant MG5 MR Luxury if self.series.startswith("EP2DP3"): return 61.1 - # ZS EV Standard 2021 - if self.series.startswith("ZS EV S"): - return 49.0 return None + + def __zs_ev_real_battery_capacity(self) -> float | None: + if self.supports_target_soc: + # Long Range with NMC battery + return 68.3 + # Standard Range with LFP battery + return 49.0