diff --git a/custom_components/tibber_prices/config_flow.py b/custom_components/tibber_prices/config_flow.py index 0f0988e..34bcdbb 100644 --- a/custom_components/tibber_prices/config_flow.py +++ b/custom_components/tibber_prices/config_flow.py @@ -88,15 +88,75 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN): """Create an options flow for this configentry.""" return TibberPricesOptionsFlowHandler() - @staticmethod - def async_get_reauth_flow(entry: ConfigEntry) -> ConfigFlow: - """Return the reauth flow handler for this integration.""" - return TibberPricesReauthFlowHandler(entry) - def is_matching(self, other_flow: dict) -> bool: """Return True if match_dict matches this flow.""" return bool(other_flow.get("domain") == DOMAIN) + async def async_step_reauth(self, entry_data: dict[str, Any]) -> ConfigFlowResult: # noqa: ARG002 + """Handle reauth flow when access token becomes invalid.""" + self._reauth_entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm(self, user_input: dict | None = None) -> ConfigFlowResult: + """Confirm reauth dialog - prompt for new access token.""" + _errors = {} + + if user_input is not None: + try: + viewer = await self._get_viewer_details(access_token=user_input[CONF_ACCESS_TOKEN]) + except TibberPricesApiClientAuthenticationError as exception: + LOGGER.warning(exception) + _errors["base"] = "auth" + except TibberPricesApiClientCommunicationError as exception: + LOGGER.error(exception) + _errors["base"] = "connection" + except TibberPricesApiClientError as exception: + LOGGER.exception(exception) + _errors["base"] = "unknown" + else: + # Validate that the new token has access to all configured homes + if self._reauth_entry: + # Get all configured home IDs (main entry + subentries) + configured_home_ids = self._get_all_configured_home_ids(self._reauth_entry) + + # Get accessible home IDs from the new token + accessible_homes = viewer.get("homes", []) + accessible_home_ids = {home["id"] for home in accessible_homes} + + # Check if all configured homes are accessible with the new token + missing_home_ids = configured_home_ids - accessible_home_ids + + if missing_home_ids: + # New token doesn't have access to all configured homes + LOGGER.error( + "New access token missing access to configured homes: %s", + ", ".join(missing_home_ids), + ) + _errors["base"] = "missing_homes" + else: + # Update the config entry with the new access token + self.hass.config_entries.async_update_entry( + self._reauth_entry, + data={ + **self._reauth_entry.data, + CONF_ACCESS_TOKEN: user_input[CONF_ACCESS_TOKEN], + }, + ) + await self.hass.config_entries.async_reload(self._reauth_entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth_confirm", + data_schema=vol.Schema( + { + vol.Required(CONF_ACCESS_TOKEN): TextSelector( + TextSelectorConfig(type=TextSelectorType.TEXT), + ), + } + ), + errors=_errors, + ) + async def async_step_user( self, user_input: dict | None = None, @@ -211,6 +271,21 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN): ), ) + def _get_all_configured_home_ids(self, main_entry: ConfigEntry) -> set[str]: + """Get all configured home IDs from main entry and all subentries.""" + home_ids = set() + + # Add home_id from main entry if it exists + if main_entry.data.get("home_id"): + home_ids.add(main_entry.data["home_id"]) + + # Add home_ids from all subentries + for entry in self.hass.config_entries.async_entries(DOMAIN): + if entry.data.get("home_id") and entry != main_entry: + home_ids.add(entry.data["home_id"]) + + return home_ids + @staticmethod def _get_home_title(home: dict) -> str: """Generate a user-friendly title for a home.""" @@ -240,52 +315,6 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN): return result["viewer"] -class TibberPricesReauthFlowHandler(ConfigFlow): - """Handle a reauthentication flow for tibber_prices.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize the reauth flow handler.""" - self._entry = entry - self._errors: dict[str, str] = {} - - async def async_step_user(self, user_input: dict | None = None) -> ConfigFlowResult: - """Prompt for a new access token.""" - if user_input is not None: - try: - await TibberPricesApiClient( - access_token=user_input[CONF_ACCESS_TOKEN], - session=async_create_clientsession(self.hass), - ).async_get_viewer_details() - except TibberPricesApiClientAuthenticationError as exception: - LOGGER.warning(exception) - self._errors["base"] = "auth" - except TibberPricesApiClientCommunicationError as exception: - LOGGER.error(exception) - self._errors["base"] = "connection" - except TibberPricesApiClientError as exception: - LOGGER.exception(exception) - self._errors["base"] = "unknown" - else: - self.hass.config_entries.async_update_entry( - self._entry, - data={ - **self._entry.data, - CONF_ACCESS_TOKEN: user_input[CONF_ACCESS_TOKEN], - }, - ) - return self.async_abort(reason="reauth_successful") - - return self.async_show_form( - step_id="user", - data_schema=vol.Schema( - { - vol.Required(CONF_ACCESS_TOKEN): TextSelector(TextSelectorConfig(type=TextSelectorType.TEXT)), - } - ), - errors=self._errors, - ) - - class TibberPricesSubentryFlowHandler(ConfigSubentryFlow): """Handle subentry flows for tibber_prices.""" diff --git a/custom_components/tibber_prices/manifest.json b/custom_components/tibber_prices/manifest.json index e39112a..65cbc2e 100644 --- a/custom_components/tibber_prices/manifest.json +++ b/custom_components/tibber_prices/manifest.json @@ -6,7 +6,7 @@ ], "config_flow": true, "documentation": "https://github.com/jpawlowski/hass.tibber_prices", - "integration_type": "hub", + "integration_type": "service", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/jpawlowski/hass.tibber_prices/issues", "requirements": [ diff --git a/custom_components/tibber_prices/translations/de.json b/custom_components/tibber_prices/translations/de.json index 35a9f58..b32cebe 100644 --- a/custom_components/tibber_prices/translations/de.json +++ b/custom_components/tibber_prices/translations/de.json @@ -23,6 +23,14 @@ }, "title": "Wähle ein Zuhause", "submit": "Zuhause auswählen" + }, + "reauth_confirm": { + "title": "Tibber-Integration erneut authentifizieren", + "description": "Der Zugriffstoken für Tibber ist nicht mehr gültig. Bitte gib einen neuen API-Zugriffstoken ein, um diese Integration weiter zu nutzen.\n\nUm einen neuen API-Zugriffstoken zu generieren, besuche https://developer.tibber.com.", + "data": { + "access_token": "API-Zugriffstoken" + }, + "submit": "Token aktualisieren" } }, "error": { @@ -30,12 +38,14 @@ "connection": "Verbindung zu Tibber nicht möglich. Bitte überprüfe deine Internetverbindung.", "unknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte überprüfe die Logs für Details.", "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_access_token": "Ungültiges Zugriffstoken" + "invalid_access_token": "Ungültiges Zugriffstoken", + "missing_homes": "Der neue Zugriffstoken hat keinen Zugriff auf alle konfigurierten Zuhause. Bitte verwende einen Zugriffstoken, der Zugriff auf die gleichen Tibber-Zuhause hat." }, "abort": { "already_configured": "Integration ist bereits konfiguriert", "entry_not_found": "Tibber Konfigurationseintrag nicht gefunden.", - "setup_complete": "Einrichtung abgeschlossen! Du kannst zusätzliche Optionen für Tibber Preise in den Integrationsoptionen ändern, nachdem du diesen Dialog geschlossen hast." + "setup_complete": "Einrichtung abgeschlossen! Du kannst zusätzliche Optionen für Tibber Preise in den Integrationsoptionen ändern, nachdem du diesen Dialog geschlossen hast.", + "reauth_successful": "Erneute Authentifizierung erfolgreich. Die Integration wurde mit dem neuen Zugriffstoken aktualisiert." } }, "config_subentries": { diff --git a/custom_components/tibber_prices/translations/en.json b/custom_components/tibber_prices/translations/en.json index 48e66be..1f738d2 100644 --- a/custom_components/tibber_prices/translations/en.json +++ b/custom_components/tibber_prices/translations/en.json @@ -23,6 +23,14 @@ }, "title": "Pick a home", "submit": "Select Home" + }, + "reauth_confirm": { + "title": "Reauthenticate Tibber Integration", + "description": "The access token for Tibber is no longer valid. Please enter a new API access token to continue using this integration.\n\nTo generate a new API access token, visit https://developer.tibber.com.", + "data": { + "access_token": "API access token" + }, + "submit": "Update Token" } }, "error": { @@ -30,12 +38,14 @@ "connection": "Unable to connect to Tibber. Please check your internet connection.", "unknown": "Unexpected error", "cannot_connect": "Failed to connect", - "invalid_access_token": "Invalid access token" + "invalid_access_token": "Invalid access token", + "missing_homes": "The new access token does not have access to all configured homes. Please use an access token that has access to the same Tibber homes." }, "abort": { "already_configured": "Integration is already configured", "entry_not_found": "Tibber configuration entry not found.", - "setup_complete": "Setup complete! You can change additional options for Tibber Prices in the integration's options after closing this dialog." + "setup_complete": "Setup complete! You can change additional options for Tibber Prices in the integration's options after closing this dialog.", + "reauth_successful": "Reauthentication successful. The integration has been updated with the new access token." } }, "config_subentries": { diff --git a/custom_components/tibber_prices/translations/nb.json b/custom_components/tibber_prices/translations/nb.json index a81ddff..4da4495 100644 --- a/custom_components/tibber_prices/translations/nb.json +++ b/custom_components/tibber_prices/translations/nb.json @@ -23,6 +23,14 @@ }, "title": "Velg et hjem", "submit": "Velg Hjem" + }, + "reauth_confirm": { + "title": "Autentiser Tibber-integrasjonen på nytt", + "description": "Tilgangstokenet for Tibber er ikke lenger gyldig. Vennligst skriv inn et nytt API-tilgangstoken for å fortsette å bruke denne integrasjonen.\n\nFor å generere et nytt API-tilgangstoken, besøk https://developer.tibber.com.", + "data": { + "access_token": "API-tilgangstoken" + }, + "submit": "Oppdater Token" } }, "error": { @@ -30,12 +38,14 @@ "connection": "Kan ikke koble til Tibber. Vennligst sjekk internettforbindelsen din.", "unknown": "Uventet feil", "cannot_connect": "Tilkobling mislyktes", - "invalid_access_token": "Ugyldig tilgangstoken" + "invalid_access_token": "Ugyldig tilgangstoken", + "missing_homes": "Det nye tilgangstokenet har ikke tilgang til alle konfigurerte hjem. Vennligst bruk et tilgangstoken som har tilgang til de samme Tibber-hjemmene." }, "abort": { "already_configured": "Integrasjonen er allerede konfigurert", "entry_not_found": "Tibber-konfigurasjonsoppføring ikke funnet.", - "setup_complete": "Oppsett fullført! Du kan endre flere alternativer for Tibber Priser i integrasjonens innstillinger etter å ha lukket denne dialogen." + "setup_complete": "Oppsett fullført! Du kan endre flere alternativer for Tibber Priser i integrasjonens innstillinger etter å ha lukket denne dialogen.", + "reauth_successful": "Autentisering på nytt vellykket. Integrasjonen har blitt oppdatert med det nye tilgangstokenet." } }, "config_subentries": { diff --git a/custom_components/tibber_prices/translations/nl.json b/custom_components/tibber_prices/translations/nl.json index 6f177a7..fcf8838 100644 --- a/custom_components/tibber_prices/translations/nl.json +++ b/custom_components/tibber_prices/translations/nl.json @@ -23,6 +23,14 @@ }, "title": "Kies een woning", "submit": "Selecteer Woning" + }, + "reauth_confirm": { + "title": "Tibber-integratie opnieuw authenticeren", + "description": "Het toegangstoken voor Tibber is niet langer geldig. Voer een nieuw API-toegangstoken in om deze integratie te blijven gebruiken.\n\nOm een nieuw API-toegangstoken te genereren, bezoek https://developer.tibber.com.", + "data": { + "access_token": "API-toegangstoken" + }, + "submit": "Token Bijwerken" } }, "error": { @@ -30,12 +38,14 @@ "connection": "Kan geen verbinding maken met Tibber. Controleer je internetverbinding.", "unknown": "Onverwachte fout", "cannot_connect": "Verbinding mislukt", - "invalid_access_token": "Ongeldig toegangstoken" + "invalid_access_token": "Ongeldig toegangstoken", + "missing_homes": "Het nieuwe toegangstoken heeft geen toegang tot alle geconfigureerde woningen. Gebruik een toegangstoken dat toegang heeft tot dezelfde Tibber-woningen." }, "abort": { "already_configured": "Integratie is al geconfigureerd", "entry_not_found": "Tibber configuratie-invoer niet gevonden.", - "setup_complete": "Installatie voltooid! Je kunt extra opties voor Tibber Prijzen wijzigen in de integratie-opties na het sluiten van dit venster." + "setup_complete": "Installatie voltooid! Je kunt extra opties voor Tibber Prijzen wijzigen in de integratie-opties na het sluiten van dit venster.", + "reauth_successful": "Herauthenticatie succesvol. De integratie is bijgewerkt met het nieuwe toegangstoken." } }, "config_subentries": { diff --git a/custom_components/tibber_prices/translations/sv.json b/custom_components/tibber_prices/translations/sv.json index 55ccfb2..8b269fa 100644 --- a/custom_components/tibber_prices/translations/sv.json +++ b/custom_components/tibber_prices/translations/sv.json @@ -23,6 +23,14 @@ }, "title": "Välj ett hem", "submit": "Välj Hem" + }, + "reauth_confirm": { + "title": "Autentisera Tibber-integrationen igen", + "description": "Åtkomsttoken för Tibber är inte längre giltigt. Vänligen ange en ny API-åtkomsttoken för att fortsätta använda denna integration.\n\nFör att generera en ny API-åtkomsttoken, besök https://developer.tibber.com.", + "data": { + "access_token": "API-åtkomsttoken" + }, + "submit": "Uppdatera Token" } }, "error": { @@ -30,12 +38,14 @@ "connection": "Kan inte ansluta till Tibber. Kontrollera din internetanslutning.", "unknown": "Oväntat fel", "cannot_connect": "Anslutning misslyckades", - "invalid_access_token": "Ogiltigt åtkomsttoken" + "invalid_access_token": "Ogiltigt åtkomsttoken", + "missing_homes": "Den nya åtkomsttoken har inte tillgång till alla konfigurerade hem. Använd en åtkomsttoken som har tillgång till samma Tibber-hem." }, "abort": { "already_configured": "Integrationen är redan konfigurerad", "entry_not_found": "Tibber-konfigurationspost hittades inte.", - "setup_complete": "Installation klar! Du kan ändra fler alternativ för Tibber Priser i integrationens alternativ efter att ha stängt denna dialog." + "setup_complete": "Installation klar! Du kan ändra fler alternativ för Tibber Priser i integrationens alternativ efter att ha stängt denna dialog.", + "reauth_successful": "Återautentisering lyckades. Integrationen har uppdaterats med den nya åtkomsttoken." } }, "config_subentries": {