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
This commit is contained in:
Ron Klinkien
2025-04-19 10:38:01 +00:00
parent 84ad53891b
commit 9cf1b3a3bb
6 changed files with 27 additions and 20 deletions

View File

@@ -15,9 +15,9 @@ from garminconnect import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_TOKEN from homeassistant.const import CONF_TOKEN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
import binascii
from .const import ( from .const import (
DATA_COORDINATOR, DATA_COORDINATOR,
DAY_TO_NUMBER, DAY_TO_NUMBER,
@@ -84,11 +84,15 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
async def async_login(self) -> bool: async def async_login(self) -> bool:
"""Login to Garmin Connect.""" """Login to Garmin Connect."""
try: 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]) await self.hass.async_add_executor_job(self.api.login, self.entry.data[CONF_TOKEN])
except GarminConnectAuthenticationError as err: except GarminConnectAuthenticationError as err:
_LOGGER.error( _LOGGER.error(
"Authentication error occurred during login: %s", err.response.text) "Authentication error occurred during login: %s", err.response.text)
return False raise ConfigEntryAuthFailed from err
except GarminConnectTooManyRequestsError as err: except GarminConnectTooManyRequestsError as err:
_LOGGER.error( _LOGGER.error(
"Too many request error occurred during login: %s", err) "Too many request error occurred during login: %s", err)
@@ -101,7 +105,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
if err.response.status_code == 401: if err.response.status_code == 401:
_LOGGER.error( _LOGGER.error(
"Authentication error occurred during login: %s", err.response.text) "Authentication error occurred during login: %s", err.response.text)
return False raise ConfigEntryAuthFailed from err
if err.response.status_code == 429: if err.response.status_code == 429:
_LOGGER.error( _LOGGER.error(
"Too many requests error occurred during login: %s", err.response.text) "Too many requests error occurred during login: %s", err.response.text)
@@ -109,6 +113,10 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
_LOGGER.error( _LOGGER.error(
"Unknown HTTP error occurred during login: %s", err) "Unknown HTTP error occurred during login: %s", err)
return False 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 except Exception as err: # pylint: disable=broad-except
_LOGGER.exception( _LOGGER.exception(
"Unknown error occurred during login: %s", err) "Unknown error occurred during login: %s", err)
@@ -254,7 +262,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
except GarminConnectAuthenticationError as err: except GarminConnectAuthenticationError as err:
_LOGGER.error( _LOGGER.error(
"Authentication error occurred during update: %s", err.response.text) "Authentication error occurred during update: %s", err.response.text)
return False raise ConfigEntryAuthFailed from err
except GarminConnectTooManyRequestsError as err: except GarminConnectTooManyRequestsError as err:
_LOGGER.error( _LOGGER.error(
"Too many request error occurred during update: %s", err) "Too many request error occurred during update: %s", err)
@@ -267,7 +275,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
if err.response.status_code == 401: if err.response.status_code == 401:
_LOGGER.error( _LOGGER.error(
"Authentication error occurred during update: %s", err.response.text) "Authentication error occurred during update: %s", err.response.text)
return False raise ConfigEntryAuthFailed from err
if err.response.status_code == 429: if err.response.status_code == 429:
_LOGGER.error( _LOGGER.error(
"Too many requests error occurred during update: %s", err.response.text) "Too many requests error occurred during update: %s", err.response.text)

View File

@@ -13,6 +13,7 @@ from garminconnect import (
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ID, CONF_TOKEN, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_ID, CONF_TOKEN, CONF_PASSWORD, CONF_USERNAME
import voluptuous as vol import voluptuous as vol
import garth
from .const import CONF_MFA, DOMAIN from .const import CONF_MFA, DOMAIN
@@ -57,8 +58,6 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
try: try:
self._login_result1, self._login_result2 = await self.hass.async_add_executor_job(self._api.login) 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 if self._login_result1 == "needs_mfa": # MFA is required
return await self.async_step_mfa() return await self.async_step_mfa()
@@ -89,12 +88,10 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async def _async_garmin_connect_mfa_login(self) -> ConfigFlowResult: async def _async_garmin_connect_mfa_login(self) -> ConfigFlowResult:
"""Handle multi-factor authentication (MFA) login with Garmin Connect.""" """Handle multi-factor authentication (MFA) login with Garmin Connect."""
try: 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) except garth.exc.GarthException as err:
_LOGGER.error(f"Error during MFA login: {err}")
_LOGGER.info(f"Oauth1: {oauth1}, Oauth2: {oauth2}")
except GarminConnectAuthenticationError:
return self.async_show_form( return self.async_show_form(
step_id="mfa", step_id="mfa",
data_schema=vol.Schema(self.mfa_data_schema), data_schema=vol.Schema(self.mfa_data_schema),

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues", "issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues",
"requirements": ["garminconnect>=0.2.26"], "requirements": ["garminconnect>=0.2.26"],
"version": "0.2.31-beta-03" "version": "0.2.31-beta-04"
} }

View File

@@ -244,7 +244,7 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
activities = self.coordinator.data.get(self._type, []) activities = self.coordinator.data.get(self._type, [])
sorted_activities = sorted( sorted_activities = sorted(
activities, key=lambda x: x["activityId"]) activities, key=lambda x: x["activityId"])
attributes["last_activities"] = sorted_activities[-10:] attributes["last_activities"] = sorted_activities[-5:]
if self._type == "lastActivity": if self._type == "lastActivity":
attributes = {**attributes, **self.coordinator.data[self._type]} attributes = {**attributes, **self.coordinator.data[self._type]}

View File

@@ -25,7 +25,7 @@
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "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%]", "unknown": "[%key:common::config_flow::error::unknown%]",
"invalid_mfa_code": "Invalid MFA code" "invalid_mfa_code": "Invalid MFA code"
}, },

View File

@@ -24,13 +24,15 @@
} }
}, },
"abort": { "abort": {
"already_configured": "Account is already configured" "already_configured": "Account is already configured",
"reauth_successful": "Reauthentication successful"
}, },
"error": { "error": {
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication", "invalid_auth": "Invalid authentication",
"too_many_requests": "Too many requests, retry later.", "too_many_requests": "Too many requests, retry later",
"unknown": "Unexpected error" "unknown": "Unexpected error",
"invalid_mfa_code": "Invalid MFA code"
} }
} }
} }