From 9cf1b3a3bb05b6162765796b864d2b3c83a8a9d6 Mon Sep 17 00:00:00 2001 From: Ron Klinkien Date: Sat, 19 Apr 2025 10:38:01 +0000 Subject: [PATCH] Migrate from old config flow to new config flow Catch authentication errors and show them in the UI Limit last activity to 5 in attributes --- custom_components/garmin_connect/__init__.py | 22 +++++++++++++------ .../garmin_connect/config_flow.py | 11 ++++------ .../garmin_connect/manifest.json | 2 +- custom_components/garmin_connect/sensor.py | 2 +- custom_components/garmin_connect/strings.json | 2 +- .../garmin_connect/translations/en.json | 8 ++++--- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/custom_components/garmin_connect/__init__.py b/custom_components/garmin_connect/__init__.py index e66cd64..7417a93 100644 --- a/custom_components/garmin_connect/__init__.py +++ b/custom_components/garmin_connect/__init__.py @@ -15,9 +15,9 @@ from garminconnect import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_TOKEN from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed - +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +import binascii from .const import ( DATA_COORDINATOR, DAY_TO_NUMBER, @@ -84,11 +84,15 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): async def async_login(self) -> bool: """Login to Garmin Connect.""" try: + # Check if the token exists in the entry data + if CONF_TOKEN not in self.entry.data: + raise KeyError("Token not found, migrating config entry") + await self.hass.async_add_executor_job(self.api.login, self.entry.data[CONF_TOKEN]) except GarminConnectAuthenticationError as err: _LOGGER.error( "Authentication error occurred during login: %s", err.response.text) - return False + raise ConfigEntryAuthFailed from err except GarminConnectTooManyRequestsError as err: _LOGGER.error( "Too many request error occurred during login: %s", err) @@ -101,7 +105,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): if err.response.status_code == 401: _LOGGER.error( "Authentication error occurred during login: %s", err.response.text) - return False + raise ConfigEntryAuthFailed from err if err.response.status_code == 429: _LOGGER.error( "Too many requests error occurred during login: %s", err.response.text) @@ -109,6 +113,10 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): _LOGGER.error( "Unknown HTTP error occurred during login: %s", err) return False + except KeyError as err: + _LOGGER.error( + "Found old config during login: %s", err) + raise ConfigEntryAuthFailed from err except Exception as err: # pylint: disable=broad-except _LOGGER.exception( "Unknown error occurred during login: %s", err) @@ -254,7 +262,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): except GarminConnectAuthenticationError as err: _LOGGER.error( "Authentication error occurred during update: %s", err.response.text) - return False + raise ConfigEntryAuthFailed from err except GarminConnectTooManyRequestsError as err: _LOGGER.error( "Too many request error occurred during update: %s", err) @@ -267,7 +275,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): if err.response.status_code == 401: _LOGGER.error( "Authentication error occurred during update: %s", err.response.text) - return False + raise ConfigEntryAuthFailed from err if err.response.status_code == 429: _LOGGER.error( "Too many requests error occurred during update: %s", err.response.text) diff --git a/custom_components/garmin_connect/config_flow.py b/custom_components/garmin_connect/config_flow.py index d60fd83..6a70c53 100644 --- a/custom_components/garmin_connect/config_flow.py +++ b/custom_components/garmin_connect/config_flow.py @@ -13,6 +13,7 @@ from garminconnect import ( from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_ID, CONF_TOKEN, CONF_PASSWORD, CONF_USERNAME import voluptuous as vol +import garth from .const import CONF_MFA, DOMAIN @@ -57,8 +58,6 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN): try: self._login_result1, self._login_result2 = await self.hass.async_add_executor_job(self._api.login) - _LOGGER.debug(f"Login result1: {self._login_result1}") - _LOGGER.debug(f"Login result2: {self._login_result2}") if self._login_result1 == "needs_mfa": # MFA is required return await self.async_step_mfa() @@ -89,12 +88,10 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN): async def _async_garmin_connect_mfa_login(self) -> ConfigFlowResult: """Handle multi-factor authentication (MFA) login with Garmin Connect.""" try: + await self.hass.async_add_executor_job(self._api.resume_login, self._login_result2, self._mfa_code) - oauth1, oauth2 = await self.hass.async_add_executor_job(self._api.resume_login, self._login_result2, self._mfa_code) - - _LOGGER.info(f"Oauth1: {oauth1}, Oauth2: {oauth2}") - - except GarminConnectAuthenticationError: + except garth.exc.GarthException as err: + _LOGGER.error(f"Error during MFA login: {err}") return self.async_show_form( step_id="mfa", data_schema=vol.Schema(self.mfa_data_schema), diff --git a/custom_components/garmin_connect/manifest.json b/custom_components/garmin_connect/manifest.json index be01bf2..1dbd31c 100644 --- a/custom_components/garmin_connect/manifest.json +++ b/custom_components/garmin_connect/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues", "requirements": ["garminconnect>=0.2.26"], - "version": "0.2.31-beta-03" + "version": "0.2.31-beta-04" } diff --git a/custom_components/garmin_connect/sensor.py b/custom_components/garmin_connect/sensor.py index 0dce9cb..23b5228 100644 --- a/custom_components/garmin_connect/sensor.py +++ b/custom_components/garmin_connect/sensor.py @@ -244,7 +244,7 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity): activities = self.coordinator.data.get(self._type, []) sorted_activities = sorted( activities, key=lambda x: x["activityId"]) - attributes["last_activities"] = sorted_activities[-10:] + attributes["last_activities"] = sorted_activities[-5:] if self._type == "lastActivity": attributes = {**attributes, **self.coordinator.data[self._type]} diff --git a/custom_components/garmin_connect/strings.json b/custom_components/garmin_connect/strings.json index 5b8c694..21e80c6 100644 --- a/custom_components/garmin_connect/strings.json +++ b/custom_components/garmin_connect/strings.json @@ -25,7 +25,7 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "too_many_requests": "Too many requests, retry later.", + "too_many_requests": "Too many requests, retry later", "unknown": "[%key:common::config_flow::error::unknown%]", "invalid_mfa_code": "Invalid MFA code" }, diff --git a/custom_components/garmin_connect/translations/en.json b/custom_components/garmin_connect/translations/en.json index 60209bc..b4e2dc0 100644 --- a/custom_components/garmin_connect/translations/en.json +++ b/custom_components/garmin_connect/translations/en.json @@ -24,13 +24,15 @@ } }, "abort": { - "already_configured": "Account is already configured" + "already_configured": "Account is already configured", + "reauth_successful": "Reauthentication successful" }, "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", - "too_many_requests": "Too many requests, retry later.", - "unknown": "Unexpected error" + "too_many_requests": "Too many requests, retry later", + "unknown": "Unexpected error", + "invalid_mfa_code": "Invalid MFA code" } } } \ No newline at end of file