Skip to content

Commit

Permalink
Merge pull request #254 from sander1988/develop
Browse files Browse the repository at this point in the history
#250 (develop > master)
  • Loading branch information
sander1988 authored Dec 8, 2024
2 parents 2daf544 + 1eb378d commit 8269c3e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 21 deletions.
13 changes: 11 additions & 2 deletions custom_components/indego/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import asyncio
import logging
from datetime import datetime, timedelta
from aiohttp.client_exceptions import ClientResponseError

import homeassistant.util.dt
import voluptuous as vol
from homeassistant.core import HomeAssistant, CoreState
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ConfigEntryAuthFailed
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
Expand Down Expand Up @@ -233,6 +234,15 @@ async def load_platforms():

try:
await indego_hub.update_generic_data_and_load_platforms(load_platforms)

except ClientResponseError as exc:
if 400 <= exc.status < 500:
_LOGGER.debug("Received 401, triggering ConfigEntryAuthFailed in HA...")
raise ConfigEntryAuthFailed from exc

_LOGGER.warning("Login unsuccessful: %s", str(exc))
return False

except AttributeError as exc:
_LOGGER.warning("Login unsuccessful: %s", str(exc))
return False
Expand Down Expand Up @@ -547,7 +557,6 @@ async def refresh_state(self):
except Exception as exc:
update_failed = True
_LOGGER.warning("Mower state update failed, reason: %s", str(exc))
#_LOGGER.exception(exc)
self.set_online_state(False)

if self._shutdown:
Expand Down
75 changes: 61 additions & 14 deletions custom_components/indego/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Final, Any
from collections.abc import Mapping
import logging

import voluptuous as vol
Expand All @@ -8,7 +9,7 @@
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.components.application_credentials import ClientCredential, async_import_client_credential
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.config_entries import OptionsFlowWithConfigEntry, ConfigEntry
from homeassistant.config_entries import OptionsFlowWithConfigEntry, ConfigEntry, ConfigFlowResult, SOURCE_REAUTH, UnknownEntry
from homeassistant.core import callback

from pyIndego import IndegoAsyncClient
Expand Down Expand Up @@ -103,7 +104,7 @@ class IndegoFlowHandler(config_entry_oauth2_flow.AbstractOAuth2FlowHandler, doma

DOMAIN = DOMAIN
VERSION = 1
_config = {}
_data = {}
_options = {}
_mower_serials = None

Expand Down Expand Up @@ -143,7 +144,10 @@ async def async_step_user(

async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult:
"""Test connection and load the available mowers."""
self._config = data
if self.source == SOURCE_REAUTH:
self._data.update(data)
else:
self._data = data
return await self.async_step_advanced()

async def async_step_advanced(
Expand All @@ -154,14 +158,17 @@ async def async_step_advanced(
_LOGGER.debug("Testing API access by retrieving available mowers...")

api_client = IndegoAsyncClient(
token=self._config["token"]["access_token"],
token=self._data["token"]["access_token"],
session=async_get_clientsession(self.hass),
raise_request_exceptions=True
)
if not default_user_agent_in_config(user_input):
self._options[CONF_USER_AGENT] = user_input[CONF_USER_AGENT]
api_client.set_default_header(HTTP_HEADER_USER_AGENT, user_input[CONF_USER_AGENT])

self._options[CONF_EXPOSE_INDEGO_AS_MOWER] = user_input[CONF_EXPOSE_INDEGO_AS_MOWER]
self._options[CONF_EXPOSE_INDEGO_AS_VACUUM] = user_input[CONF_EXPOSE_INDEGO_AS_VACUUM]

try:
self._mower_serials = await api_client.get_mowers()
_LOGGER.debug("Found mowers in account: %s", self._mower_serials)
Expand All @@ -173,21 +180,37 @@ async def async_step_advanced(
_LOGGER.error("Error while retrieving mower serial in account! Reason: %s", str(exc))
return self.async_abort(reason="connection_error")

if self.source == SOURCE_REAUTH:
if self._data[CONF_MOWER_SERIAL] not in self._mower_serials:
return self.async_abort(reason="mower_not_found")

self.async_set_unique_id(self._data[CONF_MOWER_SERIAL])
self._abort_if_unique_id_mismatch()

_LOGGER.debug("Reauth entry with data: '%s'", self._data)
_LOGGER.debug("Reauth entry with options: '%s'", self._options)

return self.async_update_reload_and_abort(
self._get_reauth_entry(),
data_updates=self._data,
options=self._options,
)

return await self.async_step_mower()

schema = vol.Schema(
{
vol.Optional(
CONF_USER_AGENT,
description={
"suggested_value": HTTP_HEADER_USER_AGENT_DEFAULT
"suggested_value": (self._options[CONF_USER_AGENT] if CONF_USER_AGENT in self._options else HTTP_HEADER_USER_AGENT_DEFAULT)
},
): str,
vol.Optional(
CONF_EXPOSE_INDEGO_AS_MOWER, default=False
CONF_EXPOSE_INDEGO_AS_MOWER, default=(self._options[CONF_EXPOSE_INDEGO_AS_MOWER] if CONF_EXPOSE_INDEGO_AS_MOWER in self._options else False)
): bool,
vol.Optional(
CONF_EXPOSE_INDEGO_AS_VACUUM, default=False
CONF_EXPOSE_INDEGO_AS_VACUUM, default=(self._options[CONF_EXPOSE_INDEGO_AS_VACUUM] if CONF_EXPOSE_INDEGO_AS_VACUUM in self._options else False)
): bool,
}
)
Expand All @@ -203,18 +226,17 @@ async def async_step_mower(
await self.async_set_unique_id(user_input[CONF_MOWER_SERIAL])
self._abort_if_unique_id_configured()

self._config[CONF_MOWER_SERIAL] = user_input[CONF_MOWER_SERIAL]
self._config[CONF_MOWER_NAME] = user_input[CONF_MOWER_NAME]
self._data[CONF_MOWER_SERIAL] = user_input[CONF_MOWER_SERIAL]
self._data[CONF_MOWER_NAME] = user_input[CONF_MOWER_NAME]

_LOGGER.debug("Creating entry with config: '%s'", self._config)
_LOGGER.debug("Creating entry with data: '%s'", self._data)
_LOGGER.debug("Creating entry with options: '%s'", self._options)

result = self.async_create_entry(
return self.async_create_entry(
title=("%s (%s)" % (user_input[CONF_MOWER_NAME], user_input[CONF_MOWER_SERIAL])),
data=self._config
data=self._data,
options=self._options,
)
result["options"] = self._options
return result

return self.async_show_form(
step_id="mower",
Expand All @@ -223,6 +245,31 @@ async def async_step_mower(
last_step=True
)

async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon an API authentication error."""
current_config = self._get_reauth_entry()

self._data = dict(current_config.data)
self._options = dict(current_config.options)

_LOGGER.debug("Loaded reauth with data: '%s'", self._data)
_LOGGER.debug("Loaded reauth with options: '%s'", self._options)

return await self.async_step_reauth_confirm()

async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema({}),
)
return await self.async_step_user()

def _build_mower_options_schema(self):
return vol.Schema(
{
Expand Down
2 changes: 1 addition & 1 deletion custom_components/indego/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"codeowners": ["@jm-73", "@eavanvalkenburg", "@sander1988"],
"requirements": ["pyIndego==3.2.2"],
"iot_class": "cloud_push",
"version": "5.7.7",
"version": "5.7.8",
"loggers": ["custom_components.indego", "pyIndego"]
}
7 changes: 6 additions & 1 deletion custom_components/indego/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"abort": {
"already_configured": "Dieser Bosch Indego Mähroboter wurde bereits konfiguriert!",
"connection_error": "Die Verbindung zur Bosch Indego API ist fehlgeschlagen! Bitte die Known Issues Seite (https://github.com/sander1988/Indego?tab=readme-ov-file#known-issues) für möglche Lösungen nutzen.",
"no_mowers_found": "In diesem Bosch Indego Account wurden keine Mähroboter gefunden!"
"no_mowers_found": "In diesem Bosch Indego Account wurden keine Mähroboter gefunden!",
"reauth_successful": "Re-Authentifizierung war erfolgreich. Zugang zur Bosch API wurde wiederhergestellt."
},
"step": {
"advanced": {
Expand All @@ -20,6 +21,10 @@
"mower_name": "Mähroboter Name"
},
"description": "Bitte die Seriennummer des Bosch Mähroboters, der hinzugefügt werden soll, auswählen."
},
"reauth_confirm": {
"title": "Authentifizierung abgelaufen",
"description": "Die Bosch Indego API Authentifizierung ist abgelaufen. Bitte mit der Bosch SingleKey ID neu Authentifizieren."
}
}
},
Expand Down
7 changes: 6 additions & 1 deletion custom_components/indego/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"abort": {
"already_configured": "This Bosch Indego mower has already been configured!",
"connection_error": "The connection to the Bosch Indego API failed! Please use the known issues page (https://github.com/sander1988/Indego?tab=readme-ov-file#known-issues) for possible solutions.",
"no_mowers_found": "No mowers found in this Bosch Indego account!"
"no_mowers_found": "No mowers found in this Bosch Indego account!",
"reauth_successful": "Re-authentication was successful. Access to the Bosch API has been restored."
},
"step": {
"advanced": {
Expand All @@ -20,6 +21,10 @@
"mower_name": "Mower name"
},
"description": "Please select the serial of the Bosch Mower your would like to add."
},
"reauth_confirm": {
"title": "Authentication expired",
"description": "The Bosch Indego API authentication has expired. Please re-authenticate using your Bosch SingleKey ID."
}
}
},
Expand Down
7 changes: 6 additions & 1 deletion custom_components/indego/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"abort": {
"already_configured": "Cette tondeuse Bosch Indego a déjà été configurée !",
"connection_error": "La connexion à l'API Bosch Indego a échoué ! Regardez la page des problèmes connus (https://github.com/sander1988/Indego?tab=readme-ov-file#known-issues) pour trouver une solution éventuelle.",
"no_mowers_found": "Aucune tondeuse n'a été trouvée sur ce compte Bosch Indego !"
"no_mowers_found": "Aucune tondeuse n'a été trouvée sur ce compte Bosch Indego !",
"reauth_successful": "Ré-authentication réussie. L'accès à l'API Bosch a été rétabli."
},
"step": {
"advanced": {
Expand All @@ -20,6 +21,10 @@
"mower_name": "Nom de la tondeuse"
},
"description": "Sélectionez le numéro de série de la tondeuse Bosch que vous souhaitez ajouter."
},
"reauth_confirm": {
"title": "Authentication expirée",
"description": "L'authentification de l'API Bosch Indego a expiré. Re-authentifiez-vous avec votre Bosch SingleKey ID."
}
}
},
Expand Down
7 changes: 6 additions & 1 deletion custom_components/indego/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"abort": {
"already_configured": "Deze Bosch Indego robotmaaier is al geconfigureerd!",
"connection_error": "De verbinding met de Bosch Indego API is mislukt! Gebruik de bekende problemen pagina (https://github.com/sander1988/Indego?tab=readme-ov-file#known-issues) voor mogelijke oplossingen.",
"no_mowers_found": "Geen robotmaaiers gevonden in deze Bosch Indego account!"
"no_mowers_found": "Geen robotmaaiers gevonden in deze Bosch Indego account!",
"reauth_successful": "Herauthenticatie is gelukt. De toegang tot de Bosch API is hersteld."
},
"step": {
"advanced": {
Expand All @@ -20,6 +21,10 @@
"mower_name": "Robotmaaier naam"
},
"description": "Selecteer het serienummer van de Bosch Indego robotmaaier die je toe wilt voegen."
},
"reauth_confirm": {
"title": "Authenticatie verlopen",
"description": "De Bosch Indego API authenticatie is verlopen. Log a.u.b. opnieuw in m.b.v. je Bosch SingleKey ID."
}
}
},
Expand Down

0 comments on commit 8269c3e

Please sign in to comment.