From bbcad17d36660d128272d63f64ca9aa4a477058d Mon Sep 17 00:00:00 2001 From: Nils Vogels Date: Fri, 13 Dec 2024 09:21:29 +0100 Subject: [PATCH 1/5] Only connect mqtt when the integration is loaded --- custom_components/myskoda/coordinator.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/custom_components/myskoda/coordinator.py b/custom_components/myskoda/coordinator.py index 785c18d..80e6340 100644 --- a/custom_components/myskoda/coordinator.py +++ b/custom_components/myskoda/coordinator.py @@ -8,7 +8,7 @@ from aiohttp import ClientError from aiohttp.client_exceptions import ClientResponseError -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -121,8 +121,10 @@ async def _async_update_data(self) -> State: config = self.data.config if self.data and self.data.config else Config() operations = self.operations - if not self.myskoda.mqtt and not self._mqtt_connecting: - self.hass.async_create_task(self._mqtt_connect()) + if self.config.state == ConfigEntryState.LOADED: + # Make sure we are ready for prime time, connect to MQTT + if not self.myskoda.mqtt and not self._mqtt_connecting: + self.hass.async_create_task(self._mqtt_connect()) _LOGGER.debug("Performing scheduled update of all data for vin %s", self.vin) From f0e1878984ac4ad3ae85323d51c792fc9f5fcd9f Mon Sep 17 00:00:00 2001 From: Nils Vogels Date: Fri, 13 Dec 2024 10:05:00 +0100 Subject: [PATCH 2/5] Further segregation of coordinator startup: - Seperate flow for setup phase - Schedule MQTT connection only after integration has initialized - Wrap MQTT startup in try/except for proper unlocking --- custom_components/myskoda/coordinator.py | 87 ++++++++++++++++-------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/custom_components/myskoda/coordinator.py b/custom_components/myskoda/coordinator.py index 80e6340..2c9d03d 100644 --- a/custom_components/myskoda/coordinator.py +++ b/custom_components/myskoda/coordinator.py @@ -11,6 +11,7 @@ from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers.debounce import Debouncer +from homeassistant.helpers.start import async_at_started from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from myskoda import MySkoda, Vehicle @@ -115,16 +116,63 @@ def __init__( self.update_positions = self._debounce(self._update_positions) self._mqtt_connecting: bool = False + async def _async_get_vehicle_data(self) -> Vehicle: + """Internal method that fetches vehicle data.""" + if self.data: + if ( + self.data.vehicle.info.device_platform == "MBB" + and self.data.vehicle.info.specification.model == "CitigoE iV" + ): + _LOGGER.debug( + "Detected Citigo iV, requesting only partial update without health" + ) + vehicle = await self.myskoda.get_partial_vehicle( + self.vin, + [ + CapabilityId.AIR_CONDITIONING, + CapabilityId.AUXILIARY_HEATING, + CapabilityId.CHARGING, + CapabilityId.PARKING_POSITION, + CapabilityId.STATE, + CapabilityId.TRIP_STATISTICS, + ], + ) + else: + vehicle = await self.myskoda.get_vehicle(self.vin) + else: + vehicle = await self.myskoda.get_vehicle(self.vin) + return vehicle + async def _async_update_data(self) -> State: vehicle = None user = None config = self.data.config if self.data and self.data.config else Config() operations = self.operations - if self.config.state == ConfigEntryState.LOADED: - # Make sure we are ready for prime time, connect to MQTT - if not self.myskoda.mqtt and not self._mqtt_connecting: - self.hass.async_create_task(self._mqtt_connect()) + if self.config.state == ConfigEntryState.SETUP_IN_PROGRESS: + _LOGGER.debug("Performing initial data fetch for vin %s", self.vin) + try: + user = await self.myskoda.get_user() + vehicle = await self._async_get_vehicle_data() + except ClientResponseError as err: + handle_aiohttp_error( + "setup user and vehicle", err, self.hass, self.config + ) + raise UpdateFailed("Failed to retrieve initial data during setup") + + def _async_finish_startup(self) -> None: + """Tasks to execute when we have finished starting up.""" + if not self.myskoda.mqtt and not self._mqtt_connecting: + self.hass.async_create_task(self._mqtt_connect()) + + async_at_started( + self.hass, _async_finish_startup + ) # Schedule post-setup tasks + return State(vehicle, user, config, operations) + + # Regular update + if not self.myskoda.mqtt and not self._mqtt_connecting: + self.hass.async_create_task(self._mqtt_connect()) _LOGGER.debug("Performing scheduled update of all data for vin %s", self.vin) @@ -140,29 +188,7 @@ async def _async_update_data(self) -> State: # Obtain vehicle data. try: - if self.data: - if ( - self.data.vehicle.info.device_platform == "MBB" - and self.data.vehicle.info.specification.model == "CitigoE iV" - ): - _LOGGER.debug( - "Detected CitigoE iV, requesting only partial update without health" - ) - vehicle = await self.myskoda.get_partial_vehicle( - self.vin, - [ - CapabilityId.AIR_CONDITIONING, - CapabilityId.AUXILIARY_HEATING, - CapabilityId.CHARGING, - CapabilityId.PARKING_POSITION, - CapabilityId.STATE, - CapabilityId.TRIP_STATISTICS, - ], - ) - else: - vehicle = await self.myskoda.get_vehicle(self.vin) - else: - vehicle = await self.myskoda.get_vehicle(self.vin) + vehicle = await self._async_get_vehicle_data() except ClientResponseError as err: handle_aiohttp_error("vehicle", err, self.hass, self.config) except ClientError as err: @@ -176,8 +202,11 @@ async def _mqtt_connect(self) -> None: """Connect to MQTT and handle internals.""" _LOGGER.debug("Connecting to MQTT.") self._mqtt_connecting = True - await self.myskoda.enable_mqtt() - self.myskoda.subscribe(self._on_mqtt_event) + try: + await self.myskoda.enable_mqtt() + self.myskoda.subscribe(self._on_mqtt_event) + except Exception: + pass self._mqtt_connecting = False async def _on_mqtt_event(self, event: Event) -> None: From 5784c75e8d0fbcde4bcc27dc3a778127a78f6e5e Mon Sep 17 00:00:00 2001 From: Nils Vogels Date: Mon, 30 Dec 2024 22:53:57 +0100 Subject: [PATCH 3/5] Add minimal startup, requesting only basic data for vehicles --- custom_components/myskoda/coordinator.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/custom_components/myskoda/coordinator.py b/custom_components/myskoda/coordinator.py index 88e5662..a56db2a 100644 --- a/custom_components/myskoda/coordinator.py +++ b/custom_components/myskoda/coordinator.py @@ -120,6 +120,10 @@ def __init__( self.update_departure_info = self._debounce(self._update_departure_info) self._mqtt_connecting: bool = False + async def _async_get_minimal_data(self) -> Vehicle: + """Internal method that fetches only basic vehicle info.""" + return await self.myskoda.get_partial_vehicle(self.vin, []) + async def _async_get_vehicle_data(self) -> Vehicle: """Internal method that fetches vehicle data.""" if self.data: @@ -157,7 +161,7 @@ async def _async_update_data(self) -> State: _LOGGER.debug("Performing initial data fetch for vin %s", self.vin) try: user = await self.myskoda.get_user() - vehicle = await self._async_get_vehicle_data() + vehicle = await self._async_get_minimal_data() except ClientResponseError as err: handle_aiohttp_error( "setup user and vehicle", err, self.hass, self.config @@ -166,6 +170,9 @@ async def _async_update_data(self) -> State: def _async_finish_startup(self) -> None: """Tasks to execute when we have finished starting up.""" + _LOGGER.debug( + "MySkoda has finished starting up. Scheduling post-start tasks." + ) if not self.myskoda.mqtt and not self._mqtt_connecting: self.hass.async_create_task(self._mqtt_connect()) From 5215c124785a6c4c062decc9bd27f41e72379d57 Mon Sep 17 00:00:00 2001 From: Nils Vogels Date: Fri, 3 Jan 2025 00:10:00 +0100 Subject: [PATCH 4/5] Add coordinator to context or post-start tasks --- custom_components/myskoda/coordinator.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/custom_components/myskoda/coordinator.py b/custom_components/myskoda/coordinator.py index 4ec1ca0..e13005d 100644 --- a/custom_components/myskoda/coordinator.py +++ b/custom_components/myskoda/coordinator.py @@ -35,6 +35,7 @@ from .const import ( API_COOLDOWN_IN_SECONDS, CONF_POLL_INTERVAL, + COORDINATORS, DEFAULT_FETCH_INTERVAL_IN_MINUTES, DOMAIN, MAX_STORED_OPERATIONS, @@ -168,22 +169,29 @@ async def _async_update_data(self) -> State: ) raise UpdateFailed("Failed to retrieve initial data during setup") - def _async_finish_startup(self) -> None: + def _async_finish_startup(hass, config, vin) -> None: """Tasks to execute when we have finished starting up.""" _LOGGER.debug( - "MySkoda has finished starting up. Scheduling post-start tasks." + "MySkoda has finished starting up. Scheduling post-start tasks for vin %s.", + vin, ) - if not self.myskoda.mqtt and not self._mqtt_connecting: - self.hass.async_create_task(self._mqtt_connect()) + try: + coord = hass.data[DOMAIN][config.entry_id][COORDINATORS][vin] + if not coord.myskoda.mqtt and not coord._mqtt_connecting: + config.async_create_background_task( + self.hass, coord._mqtt_connect(), "mqtt" + ) + except KeyError: + _LOGGER.debug("Could not connect to MQTT. Waiting for regular poll") + pass async_at_started( - self.hass, _async_finish_startup + hass=self.hass, + at_start_cb=_async_finish_startup(self.hass, self.config, self.vin), # pyright: ignore[reportArgumentType] ) # Schedule post-setup tasks return State(vehicle, user, config, operations) # Regular update - if not self.myskoda.mqtt and not self._mqtt_connecting: - self.hass.async_create_task(self._mqtt_connect()) _LOGGER.debug("Performing scheduled update of all data for vin %s", self.vin) From 5c7f3a458661a202913ffe75e8dceaed6ddfd87d Mon Sep 17 00:00:00 2001 From: WebSpider Date: Sat, 4 Jan 2025 02:14:37 +0100 Subject: [PATCH 5/5] Remove AUXILIARY_HEATING Co-authored-by: EnergyX <74157307+prvakt@users.noreply.github.com> --- custom_components/myskoda/coordinator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/custom_components/myskoda/coordinator.py b/custom_components/myskoda/coordinator.py index e13005d..ab23f37 100644 --- a/custom_components/myskoda/coordinator.py +++ b/custom_components/myskoda/coordinator.py @@ -139,7 +139,6 @@ async def _async_get_vehicle_data(self) -> Vehicle: self.vin, [ CapabilityId.AIR_CONDITIONING, - CapabilityId.AUXILIARY_HEATING, CapabilityId.CHARGING, CapabilityId.PARKING_POSITION, CapabilityId.STATE,