Skip to content

Commit

Permalink
Merge pull request #97 from MislavMandaric/feat/reauth
Browse files Browse the repository at this point in the history
Adds reauth step to config flow
  • Loading branch information
MislavMandaric authored Apr 11, 2022
2 parents a53caab + da6b9f9 commit 9420a18
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 36 deletions.
10 changes: 5 additions & 5 deletions custom_components/vaillant_vsmart/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ def hvac_action(self) -> str:
try:
if self._module.measured.temperature < self._module.measured.setpoint_temp:
return CURRENT_HVAC_HEAT
except TypeError as ex:
_LOGGER.exception(ex)
except TypeError:
pass

return CURRENT_HVAC_IDLE

Expand Down Expand Up @@ -155,7 +155,7 @@ def preset_mode(self) -> str:
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Select new HVAC operation mode."""

_LOGGER.debug(f"Setting HVAC mode to: {hvac_mode}")
_LOGGER.debug("Setting HVAC mode to: %s", hvac_mode)

if hvac_mode == HVAC_MODE_OFF:
try:
Expand Down Expand Up @@ -213,7 +213,7 @@ async def async_set_hvac_mode(self, hvac_mode: str) -> None:
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Select new HVAC preset mode."""

_LOGGER.debug(f"Setting HVAC preset mode to: {preset_mode}")
_LOGGER.debug("Setting HVAC preset mode to: %s", preset_mode)

if self._device.system_mode == SystemMode.FROSTGUARD:
return
Expand Down Expand Up @@ -266,7 +266,7 @@ async def async_set_temperature(self, **kwargs) -> None:
if new_temperature is None:
return

_LOGGER.debug(f"Setting target temperature to: {new_temperature}")
_LOGGER.debug("Setting target temperature to: %s", new_temperature)

endtime = datetime.datetime.now() + datetime.timedelta(
minutes=self._device.setpoint_default_duration
Expand Down
74 changes: 54 additions & 20 deletions custom_components/vaillant_vsmart/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@
CONF_TOKEN,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.httpx_client import get_async_client
from vaillant_netatmo_api import (
ApiException,
AuthClient,
RequestClientException,
Token,
TokenStore,
)
import voluptuous as vol
Expand All @@ -43,8 +41,23 @@ async def async_step_user(
) -> FlowResult:
"""Handle a flow initialized by the user."""

return await self._async_step_all("user", "already_configured", user_input)

async def async_step_reauth(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a reauth flow initialized when token used for API requests is invalid."""

return await self._async_step_all("reauth", "reauth_successful", user_input)

async def _async_step_all(
self, step_id: str, abort_reason: str, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Generic flow step that handles both user and reauth steps."""

if user_input is None:
return await self._show_config_form()
user_input = self._init_user_input()
return self._show_config_form(step_id, user_input)

errors = {}

Expand All @@ -61,20 +74,26 @@ async def async_step_user(
errors["base"] = "unknown"

if errors:
return await self._show_config_form(user_input, errors)
return self._show_config_form(step_id, user_input, errors)

await self.async_set_unique_id(user_input[CONF_USERNAME])
self._abort_if_unique_id_configured()
existing_entry = await self.async_set_unique_id(user_input[CONF_USERNAME])
if existing_entry:
self.hass.config_entries.async_update_entry(existing_entry, data=data)
await self.hass.config_entries.async_reload(existing_entry.entry_id)
return self.async_abort(reason=abort_reason)

return self.async_create_entry(title=user_input[CONF_USERNAME], data=data)

async def _show_config_form(
self, user_input: dict[str, Any] = {}, errors: dict[str, str] = None
):
def _show_config_form(
self,
step_id: str,
user_input: dict[str, Any],
errors: dict[str, str] = None,
) -> FlowResult:
"""Show the configuration form to edit location data."""

return self.async_show_form(
step_id="user",
step_id=step_id,
data_schema=vol.Schema(
{
vol.Required(
Expand All @@ -101,14 +120,29 @@ async def _show_config_form(
errors=errors,
)

def _init_user_input(self) -> dict[str, Any]:
"""Initializes user input data from configs init data."""

data = self.init_data

if data is None:
return {}

return {
CONF_CLIENT_ID: data.get(CONF_CLIENT_ID),
CONF_CLIENT_SECRET: data.get(CONF_CLIENT_SECRET),
CONF_USER_PREFIX: data.get(CONF_USER_PREFIX),
CONF_APP_VERSION: data.get(CONF_APP_VERSION),
}

async def _get_config_storage_data(
self, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Get config storage data from user input form data."""

token_store = TokenStore(
user_input[CONF_CLIENT_ID],
user_input[CONF_CLIENT_SECRET],
user_input.get(CONF_CLIENT_ID),
user_input.get(CONF_CLIENT_SECRET),
None,
None,
)
Expand All @@ -119,16 +153,16 @@ async def _get_config_storage_data(
)

await client.async_token(
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_USER_PREFIX],
user_input[CONF_APP_VERSION],
user_input.get(CONF_USERNAME),
user_input.get(CONF_PASSWORD),
user_input.get(CONF_USER_PREFIX),
user_input.get(CONF_APP_VERSION),
)

return {
CONF_CLIENT_ID: user_input[CONF_CLIENT_ID],
CONF_CLIENT_SECRET: user_input[CONF_CLIENT_SECRET],
CONF_USER_PREFIX: user_input[CONF_USER_PREFIX],
CONF_APP_VERSION: user_input[CONF_APP_VERSION],
CONF_CLIENT_ID: user_input.get(CONF_CLIENT_ID),
CONF_CLIENT_SECRET: user_input.get(CONF_CLIENT_SECRET),
CONF_USER_PREFIX: user_input.get(CONF_USER_PREFIX),
CONF_APP_VERSION: user_input.get(CONF_APP_VERSION),
CONF_TOKEN: token_store.token.serialize(),
}
10 changes: 7 additions & 3 deletions custom_components/vaillant_vsmart/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any

from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
Expand All @@ -19,6 +20,7 @@
MeasurementItem,
MeasurementType,
MeasurementScale,
RequestUnauthorizedException,
)

from .const import DOMAIN
Expand Down Expand Up @@ -83,9 +85,11 @@ async def _update_method(self):
self._debug_log(devices)

return VaillantData(self._client, devices)
except ApiException as e:
_LOGGER.exception(e)
raise UpdateFailed(f"Error communicating with API: {e}") from e
except RequestUnauthorizedException as ex:
raise ConfigEntryAuthFailed from ex
except ApiException as ex:
_LOGGER.exception(ex)
raise UpdateFailed(f"Error communicating with API: {ex}") from ex

async def _get_temperature_measurements_for_all_devices(
self, devices: list[Device]
Expand Down
22 changes: 18 additions & 4 deletions custom_components/vaillant_vsmart/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
"config": {
"step": {
"user": {
"description": "If you need help with the configuration have a look here: https://github.com/MislavMandaric/home-assistant-vaillant-vsmart",
"description": "If you're setting the component for the first time and need some guidelines, check out the [community page](https://community.home-assistant.io/t/added-support-for-vaillant-thermostat-how-to-integrate-in-official-release/31858). You can find out how to extract client ID and client secret there.",
"data": {
"client_id": "Client ID",
"client_secret": "Client secret",
"username": "Username",
"password": "Password",
"user_prefix": "User prefix",
"app_version": "App version"
}
},
"reauth": {
"description": "Tokens used by the component expired and you need to re-authenticate with the API again. Since your credentials are never stored, you need to enter them again.",
"data": {
"client_id": "Client ID",
"client_secret": "Client secret",
Expand All @@ -14,11 +25,14 @@
}
},
"error": {
"unknown": "Something went wrong when authenticating with the API."
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_in_progress": "Integration for provided user is already in progress.",
"already_configured": "Integration for provided user is already configured."
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
}
}
}
22 changes: 18 additions & 4 deletions custom_components/vaillant_vsmart/translations/hr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
"config": {
"step": {
"user": {
"description": "Pomoć oko konfiguracije integracije potražite na ovom linku: https://github.com/MislavMandaric/home-assistant-vaillant-vsmart",
"description": "Više informacija o postavkama komponente možete pronaći na [stranicama zajednice](https://community.home-assistant.io/t/added-support-for-vaillant-thermostat-how-to-integrate-in-official-release/31858), uključujući i način na koji možete saznati ID i tajni kod klijenta.",
"data": {
"client_id": "ID klijenta",
"client_secret": "Tajni kod klijenta",
"username": "Korisničko ime",
"password": "Lozinka",
"user_prefix": "Korisnički prefiks",
"app_version": "Verzija aplikacije"
}
},
"reauth": {
"description": "Tokeni za komunikaciju koje komponenta koristi su istekli i potrebna je ponovna autentikacija. Korisničko ime i lozinka se ne spremaju te ih je potrebno ponovno unijeti.",
"data": {
"client_id": "ID klijenta",
"client_secret": "Tajni kod klijenta",
Expand All @@ -14,11 +25,14 @@
}
},
"error": {
"unknown": "Došlo je do pogreške u komunikaciji sa serverom."
"invalid_auth": "Pogreška prilikom autentikacije",
"cannot_connect": "Pogreška u spajanju",
"unknown": "Neočekivana pogreška"
},
"abort": {
"already_in_progress": "Integracija je već u tijeku.",
"already_configured": "Integracija je već postavljena."
"reauth_successful": "Uspješna ponovna autentikacija.",
"already_in_progress": "Konfiguracija je već u tijeku.",
"already_configured": "Račun je otprije konfiguriran."
}
}
}

0 comments on commit 9420a18

Please sign in to comment.