Add Nanoleaf reauth flow (#55217)

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Milan Meulemans 2021-08-25 21:56:10 +02:00 committed by GitHub
parent fb28665cfa
commit 59d401e7b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 3 deletions

View File

@ -1,10 +1,10 @@
"""The Nanoleaf integration.""" """The Nanoleaf integration."""
from pynanoleaf.pynanoleaf import Nanoleaf, Unavailable from pynanoleaf.pynanoleaf import InvalidToken, Nanoleaf, Unavailable
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.const import CONF_HOST, CONF_TOKEN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .const import DEVICE, DOMAIN, NAME, SERIAL_NO from .const import DEVICE, DOMAIN, NAME, SERIAL_NO
from .util import pynanoleaf_get_info from .util import pynanoleaf_get_info
@ -18,6 +18,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
info = await hass.async_add_executor_job(pynanoleaf_get_info, nanoleaf) info = await hass.async_add_executor_job(pynanoleaf_get_info, nanoleaf)
except Unavailable as err: except Unavailable as err:
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
except InvalidToken as err:
raise ConfigEntryAuthFailed from err
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
DEVICE: nanoleaf, DEVICE: nanoleaf,

View File

@ -32,6 +32,8 @@ USER_SCHEMA: Final = vol.Schema(
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Nanoleaf config flow.""" """Nanoleaf config flow."""
reauth_entry: config_entries.ConfigEntry | None = None
VERSION = 1 VERSION = 1
def __init__(self) -> None: def __init__(self) -> None:
@ -73,6 +75,16 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) )
return await self.async_step_link() return await self.async_step_link()
async def async_step_reauth(self, data: dict[str, str]) -> FlowResult:
"""Handle Nanoleaf reauth flow if token is invalid."""
self.reauth_entry = cast(
config_entries.ConfigEntry,
self.hass.config_entries.async_get_entry(self.context["entry_id"]),
)
self.nanoleaf = Nanoleaf(data[CONF_HOST])
self.context["title_placeholders"] = {"name": self.reauth_entry.title}
return await self.async_step_link()
async def async_step_zeroconf( async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType self, discovery_info: DiscoveryInfoType
) -> FlowResult: ) -> FlowResult:
@ -135,6 +147,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unknown error authorizing Nanoleaf") _LOGGER.exception("Unknown error authorizing Nanoleaf")
return self.async_show_form(step_id="link", errors={"base": "unknown"}) return self.async_show_form(step_id="link", errors={"base": "unknown"})
if self.reauth_entry is not None:
self.hass.config_entries.async_update_entry(
self.reauth_entry,
data={
**self.reauth_entry.data,
CONF_TOKEN: self.nanoleaf.token,
},
)
await self.hass.config_entries.async_reload(self.reauth_entry.entry_id)
return self.async_abort(reason="reauth_successful")
return await self.async_setup_finish() return await self.async_setup_finish()
async def async_step_import(self, config: dict[str, Any]) -> FlowResult: async def async_step_import(self, config: dict[str, Any]) -> FlowResult:

View File

@ -21,6 +21,7 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_token": "[%key:common::config_flow::error::invalid_access_token%]", "invalid_token": "[%key:common::config_flow::error::invalid_access_token%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"unknown": "[%key:common::config_flow::error::unknown%]" "unknown": "[%key:common::config_flow::error::unknown%]"
} }
} }

View File

@ -4,6 +4,7 @@
"already_configured": "Device is already configured", "already_configured": "Device is already configured",
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"invalid_token": "Invalid access token", "invalid_token": "Invalid access token",
"reauth_successful": "Re-authentication was successful",
"unknown": "Unexpected error" "unknown": "Unexpected error"
}, },
"error": { "error": {

View File

@ -1,7 +1,7 @@
"""Test the Nanoleaf config flow.""" """Test the Nanoleaf config flow."""
from __future__ import annotations from __future__ import annotations
from unittest.mock import patch from unittest.mock import MagicMock, patch
from pynanoleaf import InvalidToken, NotAuthorizingNewTokens, Unavailable from pynanoleaf import InvalidToken, NotAuthorizingNewTokens, Unavailable
from pynanoleaf.pynanoleaf import NanoleafError from pynanoleaf.pynanoleaf import NanoleafError
@ -12,6 +12,8 @@ from homeassistant.components.nanoleaf.const import DOMAIN
from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.const import CONF_HOST, CONF_TOKEN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
TEST_NAME = "Canvas ADF9" TEST_NAME = "Canvas ADF9"
TEST_HOST = "192.168.0.100" TEST_HOST = "192.168.0.100"
TEST_OTHER_HOST = "192.168.0.200" TEST_OTHER_HOST = "192.168.0.200"
@ -283,6 +285,49 @@ async def test_discovery_link_unavailable(
assert result["reason"] == "cannot_connect" assert result["reason"] == "cannot_connect"
async def test_reauth(hass: HomeAssistant) -> None:
"""Test Nanoleaf reauth flow."""
nanoleaf = MagicMock()
nanoleaf.host = TEST_HOST
nanoleaf.token = TEST_TOKEN
entry = MockConfigEntry(
domain=DOMAIN,
unique_id=TEST_NAME,
data={CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_OTHER_TOKEN},
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.nanoleaf.config_flow.Nanoleaf",
return_value=nanoleaf,
), patch(
"homeassistant.components.nanoleaf.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_REAUTH,
"entry_id": entry.entry_id,
"unique_id": entry.unique_id,
},
data=entry.data,
)
assert result["type"] == "form"
assert result["step_id"] == "link"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
assert result2["type"] == "abort"
assert result2["reason"] == "reauth_successful"
assert entry.data[CONF_HOST] == TEST_HOST
assert entry.data[CONF_TOKEN] == TEST_TOKEN
async def test_import_config(hass: HomeAssistant) -> None: async def test_import_config(hass: HomeAssistant) -> None:
"""Test configuration import.""" """Test configuration import."""
with patch( with patch(