Skip to content

Commit 358f78c

Browse files
erwindounajoostlekzweckj
authored
Tado migrate to OAuth Device Flow (#140761)
* Bump PyTado 0.19.0 * Initial setup * Current state * Update to PyTado 0.18.8 * First concept for review * Fix * Fix * Fix * First concept for review * Bump PyTado to 0.18.9 * Remove redundant part * Initial test setup * Authentication exceptions * Fix * Fix * Fix * Update version to 2 * All migration code * Small tuning * Add reauth unique ID check * Add reauth test * 100% on config flow * Making tests working on new device flow * Fix * Fix * Fix * Update homeassistant/components/tado/strings.json * Update homeassistant/components/tado/strings.json --------- Co-authored-by: Joostlek <[email protected]> Co-authored-by: Josef Zweck <[email protected]>
1 parent 83a0ed4 commit 358f78c

File tree

15 files changed

+440
-400
lines changed

15 files changed

+440
-400
lines changed

homeassistant/components/tado/__init__.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@
1010
from homeassistant.config_entries import ConfigEntry
1111
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
1212
from homeassistant.core import HomeAssistant, callback
13-
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
13+
from homeassistant.exceptions import (
14+
ConfigEntryAuthFailed,
15+
ConfigEntryError,
16+
ConfigEntryNotReady,
17+
)
1418
from homeassistant.helpers import config_validation as cv
1519
from homeassistant.helpers.typing import ConfigType
1620

1721
from .const import (
1822
CONF_FALLBACK,
23+
CONF_REFRESH_TOKEN,
1924
CONST_OVERLAY_MANUAL,
2025
CONST_OVERLAY_TADO_DEFAULT,
2126
CONST_OVERLAY_TADO_MODE,
@@ -56,23 +61,34 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
5661

5762
async def async_setup_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool:
5863
"""Set up Tado from a config entry."""
64+
if CONF_REFRESH_TOKEN not in entry.data:
65+
raise ConfigEntryAuthFailed
5966

6067
_async_import_options_from_data_if_missing(hass, entry)
6168

6269
_LOGGER.debug("Setting up Tado connection")
70+
_LOGGER.debug(
71+
"Creating tado instance with refresh token: %s",
72+
entry.data[CONF_REFRESH_TOKEN],
73+
)
74+
75+
def create_tado_instance() -> tuple[Tado, str]:
76+
"""Create a Tado instance, this time with a previously obtained refresh token."""
77+
tado = Tado(saved_refresh_token=entry.data[CONF_REFRESH_TOKEN])
78+
return tado, tado.device_activation_status()
79+
6380
try:
64-
tado = await hass.async_add_executor_job(
65-
Tado,
66-
entry.data[CONF_USERNAME],
67-
entry.data[CONF_PASSWORD],
68-
)
81+
tado, device_status = await hass.async_add_executor_job(create_tado_instance)
6982
except PyTado.exceptions.TadoWrongCredentialsException as err:
7083
raise ConfigEntryError(f"Invalid Tado credentials. Error: {err}") from err
7184
except PyTado.exceptions.TadoException as err:
7285
raise ConfigEntryNotReady(f"Error during Tado setup: {err}") from err
73-
_LOGGER.debug(
74-
"Tado connection established for username: %s", entry.data[CONF_USERNAME]
75-
)
86+
if device_status != "COMPLETED":
87+
raise ConfigEntryAuthFailed(
88+
f"Device login flow status is {device_status}. Starting re-authentication."
89+
)
90+
91+
_LOGGER.debug("Tado connection established")
7692

7793
coordinator = TadoDataUpdateCoordinator(hass, entry, tado)
7894
await coordinator.async_config_entry_first_refresh()
@@ -82,11 +98,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool
8298

8399
entry.runtime_data = TadoData(coordinator, mobile_coordinator)
84100
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
85-
entry.async_on_unload(entry.add_update_listener(update_listener))
86101

87102
return True
88103

89104

105+
async def async_migrate_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool:
106+
"""Migrate old entry."""
107+
108+
if entry.version < 2:
109+
_LOGGER.debug("Migrating Tado entry to version 2. Current data: %s", entry.data)
110+
data = dict(entry.data)
111+
data.pop(CONF_USERNAME, None)
112+
data.pop(CONF_PASSWORD, None)
113+
hass.config_entries.async_update_entry(entry=entry, data=data, version=2)
114+
_LOGGER.debug("Migration to version 2 successful")
115+
return True
116+
117+
90118
@callback
91119
def _async_import_options_from_data_if_missing(
92120
hass: HomeAssistant, entry: TadoConfigEntry
@@ -106,11 +134,6 @@ def _async_import_options_from_data_if_missing(
106134
hass.config_entries.async_update_entry(entry, options=options)
107135

108136

109-
async def update_listener(hass: HomeAssistant, entry: TadoConfigEntry):
110-
"""Handle options update."""
111-
await hass.config_entries.async_reload(entry.entry_id)
112-
113-
114137
async def async_unload_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool:
115138
"""Unload a config entry."""
116139
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

0 commit comments

Comments
 (0)