From 04db8fc684d09f00f21a377ea2b8681e9762df48 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sun, 31 Jan 2021 09:23:29 +0100 Subject: [PATCH] Add checks for endpoint support and contract requirements (#113) * Add checks for endpoint support * Add checks for endpoint contract requirements * Add indication to open an issue on renault-api * Use INFO for endpoint_available --- custom_components/renault/const.py | 2 + custom_components/renault/renault_vehicle.py | 112 +++++++++++++------ 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/custom_components/renault/const.py b/custom_components/renault/const.py index f4e795a..a91e3f7 100644 --- a/custom_components/renault/const.py +++ b/custom_components/renault/const.py @@ -22,3 +22,5 @@ DEVICE_CLASS_PLUG_STATE = "renault__plug_state" DEVICE_CLASS_CHARGE_STATE = "renault__charge_state" DEVICE_CLASS_CHARGE_MODE = "renault__charge_mode" + +RENAULT_API_URL = "https://github.com/hacf-fr/renault-api/issues" diff --git a/custom_components/renault/renault_vehicle.py b/custom_components/renault/renault_vehicle.py index 0b04922..027fbb7 100644 --- a/custom_components/renault/renault_vehicle.py +++ b/custom_components/renault/renault_vehicle.py @@ -8,7 +8,7 @@ from homeassistant.helpers.typing import HomeAssistantType -from .const import DOMAIN +from .const import DOMAIN, RENAULT_API_URL from .renault_coordinator import RenaultDataUpdateCoordinator LOGGER = logging.getLogger(__name__) @@ -60,60 +60,102 @@ def distances_in_miles(self) -> bool: async def async_initialise(self) -> None: """Load available sensors.""" - self.coordinators["cockpit"] = RenaultDataUpdateCoordinator( - self.hass, - LOGGER, - # Name of the data. For logging purposes. - name=f"{self.details.vin} cockpit", - update_method=self.get_cockpit, - # Polling interval. Will only be polled if there are subscribers. - update_interval=self._scan_interval, - ) - self.coordinators["hvac_status"] = RenaultDataUpdateCoordinator( - self.hass, - LOGGER, - # Name of the data. For logging purposes. - name=f"{self.details.vin} hvac_status", - update_method=self.get_hvac_status, - # Polling interval. Will only be polled if there are subscribers. - update_interval=self._scan_interval, - ) - if self.details.uses_electricity(): - self.coordinators["battery"] = RenaultDataUpdateCoordinator( + if await self.endpoint_available("cockpit"): + self.coordinators["cockpit"] = RenaultDataUpdateCoordinator( self.hass, LOGGER, # Name of the data. For logging purposes. - name=f"{self.details.vin} battery", - update_method=self.get_battery_status, + name=f"{self.details.vin} cockpit", + update_method=self.get_cockpit, # Polling interval. Will only be polled if there are subscribers. update_interval=self._scan_interval, ) - self.coordinators["charge_mode"] = RenaultDataUpdateCoordinator( + if await self.endpoint_available("hvac-status"): + self.coordinators["hvac_status"] = RenaultDataUpdateCoordinator( self.hass, LOGGER, # Name of the data. For logging purposes. - name=f"{self.details.vin} charge_mode", - update_method=self.get_charge_mode, + name=f"{self.details.vin} hvac_status", + update_method=self.get_hvac_status, + # Polling interval. Will only be polled if there are subscribers. + update_interval=self._scan_interval, + ) + if self.details.uses_electricity(): + if await self.endpoint_available("battery-status"): + self.coordinators["battery"] = RenaultDataUpdateCoordinator( + self.hass, + LOGGER, + # Name of the data. For logging purposes. + name=f"{self.details.vin} battery", + update_method=self.get_battery_status, + # Polling interval. Will only be polled if there are subscribers. + update_interval=self._scan_interval, + ) + if await self.endpoint_available("charge-mode"): + self.coordinators["charge_mode"] = RenaultDataUpdateCoordinator( + self.hass, + LOGGER, + # Name of the data. For logging purposes. + name=f"{self.details.vin} charge_mode", + update_method=self.get_charge_mode, + # Polling interval. Will only be polled if there are subscribers. + update_interval=self._scan_interval, + ) + if await self.endpoint_available("location"): + self.coordinators["location"] = RenaultDataUpdateCoordinator( + self.hass, + LOGGER, + # Name of the data. For logging purposes. + name=f"{self.details.vin} location", + update_method=self.get_location, # Polling interval. Will only be polled if there are subscribers. update_interval=self._scan_interval, ) - self.coordinators["location"] = RenaultDataUpdateCoordinator( - self.hass, - LOGGER, - # Name of the data. For logging purposes. - name=f"{self.details.vin} location", - update_method=self.get_location, - # Polling interval. Will only be polled if there are subscribers. - update_interval=self._scan_interval, - ) for key in list(self.coordinators.keys()): await self.coordinators[key].async_refresh() if self.coordinators[key].not_supported: # Remove endpoint if it is not supported for this vehicle. del self.coordinators[key] + LOGGER.warning( + "`Not Supported` on HA coordinator %s was not caught" + " by `endpoint_available` method. It may be useful" + " to open an issue on %s", + key, + RENAULT_API_URL, + ) + elif self.coordinators[key].access_denied: # Remove endpoint if it is denied for this vehicle. del self.coordinators[key] + LOGGER.warning( + "`Access Denied` on HA coordinator %s was not caught" + " by `endpoint_available` method. It may be useful" + " to open an issue on %s", + key, + RENAULT_API_URL, + ) + + async def endpoint_available(self, endpoint: str) -> bool: + """Ensure the endpoint is available to avoid unnecessary queries.""" + if not await self._vehicle.supports_endpoint(endpoint): + LOGGER.info( + "Vehicle model %s does not appear to support endpoint '%s'." + " If you think this is a mistake, please open an issue on %s", + self.details.get_model_code(), + endpoint, + RENAULT_API_URL, + ) + return False + if not await self._vehicle.has_contract_for_endpoint(endpoint): + LOGGER.info( + "Vehicle %s does not appear to have a valid contract for endpoint '%s'." + " If you think this is a mistake, please open an issue on %s", + self.details.vin(), + endpoint, + RENAULT_API_URL, + ) + return False + return True async def get_battery_status(self) -> models.KamereonVehicleBatteryStatusData: """Get battery status information from vehicle."""