mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
feat: Implement reauthentication flow
This commit is contained in:
parent
543b1114ed
commit
433558f60b
7 changed files with 141 additions and 62 deletions
|
|
@ -88,15 +88,75 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
"""Create an options flow for this configentry."""
|
"""Create an options flow for this configentry."""
|
||||||
return TibberPricesOptionsFlowHandler()
|
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:
|
def is_matching(self, other_flow: dict) -> bool:
|
||||||
"""Return True if match_dict matches this flow."""
|
"""Return True if match_dict matches this flow."""
|
||||||
return bool(other_flow.get("domain") == DOMAIN)
|
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(
|
async def async_step_user(
|
||||||
self,
|
self,
|
||||||
user_input: dict | None = None,
|
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
|
@staticmethod
|
||||||
def _get_home_title(home: dict) -> str:
|
def _get_home_title(home: dict) -> str:
|
||||||
"""Generate a user-friendly title for a home."""
|
"""Generate a user-friendly title for a home."""
|
||||||
|
|
@ -240,52 +315,6 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
return result["viewer"]
|
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):
|
class TibberPricesSubentryFlowHandler(ConfigSubentryFlow):
|
||||||
"""Handle subentry flows for tibber_prices."""
|
"""Handle subentry flows for tibber_prices."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
],
|
],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://github.com/jpawlowski/hass.tibber_prices",
|
"documentation": "https://github.com/jpawlowski/hass.tibber_prices",
|
||||||
"integration_type": "hub",
|
"integration_type": "service",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"issue_tracker": "https://github.com/jpawlowski/hass.tibber_prices/issues",
|
"issue_tracker": "https://github.com/jpawlowski/hass.tibber_prices/issues",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
},
|
},
|
||||||
"title": "Wähle ein Zuhause",
|
"title": "Wähle ein Zuhause",
|
||||||
"submit": "Zuhause auswählen"
|
"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": {
|
"error": {
|
||||||
|
|
@ -30,12 +38,14 @@
|
||||||
"connection": "Verbindung zu Tibber nicht möglich. Bitte überprüfe deine Internetverbindung.",
|
"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.",
|
"unknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte überprüfe die Logs für Details.",
|
||||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
"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": {
|
"abort": {
|
||||||
"already_configured": "Integration ist bereits konfiguriert",
|
"already_configured": "Integration ist bereits konfiguriert",
|
||||||
"entry_not_found": "Tibber Konfigurationseintrag nicht gefunden.",
|
"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": {
|
"config_subentries": {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
},
|
},
|
||||||
"title": "Pick a home",
|
"title": "Pick a home",
|
||||||
"submit": "Select 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": {
|
"error": {
|
||||||
|
|
@ -30,12 +38,14 @@
|
||||||
"connection": "Unable to connect to Tibber. Please check your internet connection.",
|
"connection": "Unable to connect to Tibber. Please check your internet connection.",
|
||||||
"unknown": "Unexpected error",
|
"unknown": "Unexpected error",
|
||||||
"cannot_connect": "Failed to connect",
|
"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": {
|
"abort": {
|
||||||
"already_configured": "Integration is already configured",
|
"already_configured": "Integration is already configured",
|
||||||
"entry_not_found": "Tibber configuration entry not found.",
|
"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": {
|
"config_subentries": {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
},
|
},
|
||||||
"title": "Velg et hjem",
|
"title": "Velg et hjem",
|
||||||
"submit": "Velg 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": {
|
"error": {
|
||||||
|
|
@ -30,12 +38,14 @@
|
||||||
"connection": "Kan ikke koble til Tibber. Vennligst sjekk internettforbindelsen din.",
|
"connection": "Kan ikke koble til Tibber. Vennligst sjekk internettforbindelsen din.",
|
||||||
"unknown": "Uventet feil",
|
"unknown": "Uventet feil",
|
||||||
"cannot_connect": "Tilkobling mislyktes",
|
"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": {
|
"abort": {
|
||||||
"already_configured": "Integrasjonen er allerede konfigurert",
|
"already_configured": "Integrasjonen er allerede konfigurert",
|
||||||
"entry_not_found": "Tibber-konfigurasjonsoppføring ikke funnet.",
|
"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": {
|
"config_subentries": {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
},
|
},
|
||||||
"title": "Kies een woning",
|
"title": "Kies een woning",
|
||||||
"submit": "Selecteer 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": {
|
"error": {
|
||||||
|
|
@ -30,12 +38,14 @@
|
||||||
"connection": "Kan geen verbinding maken met Tibber. Controleer je internetverbinding.",
|
"connection": "Kan geen verbinding maken met Tibber. Controleer je internetverbinding.",
|
||||||
"unknown": "Onverwachte fout",
|
"unknown": "Onverwachte fout",
|
||||||
"cannot_connect": "Verbinding mislukt",
|
"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": {
|
"abort": {
|
||||||
"already_configured": "Integratie is al geconfigureerd",
|
"already_configured": "Integratie is al geconfigureerd",
|
||||||
"entry_not_found": "Tibber configuratie-invoer niet gevonden.",
|
"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": {
|
"config_subentries": {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
},
|
},
|
||||||
"title": "Välj ett hem",
|
"title": "Välj ett hem",
|
||||||
"submit": "Välj 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": {
|
"error": {
|
||||||
|
|
@ -30,12 +38,14 @@
|
||||||
"connection": "Kan inte ansluta till Tibber. Kontrollera din internetanslutning.",
|
"connection": "Kan inte ansluta till Tibber. Kontrollera din internetanslutning.",
|
||||||
"unknown": "Oväntat fel",
|
"unknown": "Oväntat fel",
|
||||||
"cannot_connect": "Anslutning misslyckades",
|
"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": {
|
"abort": {
|
||||||
"already_configured": "Integrationen är redan konfigurerad",
|
"already_configured": "Integrationen är redan konfigurerad",
|
||||||
"entry_not_found": "Tibber-konfigurationspost hittades inte.",
|
"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": {
|
"config_subentries": {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue