mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Introduce reauth flow to deCONZ (#45443)
This commit is contained in:
parent
18c7ae9a8b
commit
57fa7f926a
@ -174,6 +174,18 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return self.async_create_entry(title=self.bridge_id, data=self.deconz_config)
|
||||
|
||||
async def async_step_reauth(self, config: dict):
|
||||
"""Trigger a reauthentication flow."""
|
||||
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
|
||||
self.context["title_placeholders"] = {CONF_HOST: config[CONF_HOST]}
|
||||
|
||||
self.deconz_config = {
|
||||
CONF_HOST: config[CONF_HOST],
|
||||
CONF_PORT: config[CONF_PORT],
|
||||
}
|
||||
|
||||
return await self.async_step_link()
|
||||
|
||||
async def async_step_ssdp(self, discovery_info):
|
||||
"""Handle a discovered deCONZ bridge."""
|
||||
if (
|
||||
|
@ -4,6 +4,7 @@ import asyncio
|
||||
import async_timeout
|
||||
from pydeconz import DeconzSession, errors
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
@ -19,7 +20,7 @@ from .const import (
|
||||
DEFAULT_ALLOW_CLIP_SENSOR,
|
||||
DEFAULT_ALLOW_DECONZ_GROUPS,
|
||||
DEFAULT_ALLOW_NEW_DEVICES,
|
||||
DOMAIN,
|
||||
DOMAIN as DECONZ_DOMAIN,
|
||||
LOGGER,
|
||||
NEW_GROUP,
|
||||
NEW_LIGHT,
|
||||
@ -34,7 +35,7 @@ from .errors import AuthenticationRequired, CannotConnect
|
||||
@callback
|
||||
def get_gateway_from_config_entry(hass, config_entry):
|
||||
"""Return gateway with a matching bridge id."""
|
||||
return hass.data[DOMAIN][config_entry.unique_id]
|
||||
return hass.data[DECONZ_DOMAIN][config_entry.unique_id]
|
||||
|
||||
|
||||
class DeconzGateway:
|
||||
@ -152,7 +153,7 @@ class DeconzGateway:
|
||||
# Gateway service
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
identifiers={(DOMAIN, self.api.config.bridgeid)},
|
||||
identifiers={(DECONZ_DOMAIN, self.api.config.bridgeid)},
|
||||
manufacturer="Dresden Elektronik",
|
||||
model=self.api.config.modelid,
|
||||
name=self.api.config.name,
|
||||
@ -173,8 +174,14 @@ class DeconzGateway:
|
||||
except CannotConnect as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
LOGGER.error("Error connecting with deCONZ gateway: %s", err, exc_info=True)
|
||||
except AuthenticationRequired:
|
||||
self.hass.async_create_task(
|
||||
self.hass.config_entries.flow.async_init(
|
||||
DECONZ_DOMAIN,
|
||||
context={"source": SOURCE_REAUTH},
|
||||
data=self.config_entry.data,
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
for component in SUPPORTED_PLATFORMS:
|
||||
|
@ -15,14 +15,19 @@ from homeassistant.components.deconz.const import (
|
||||
CONF_ALLOW_DECONZ_GROUPS,
|
||||
CONF_ALLOW_NEW_DEVICES,
|
||||
CONF_MASTER_GATEWAY,
|
||||
DOMAIN,
|
||||
DOMAIN as DECONZ_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.ssdp import (
|
||||
ATTR_SSDP_LOCATION,
|
||||
ATTR_UPNP_MANUFACTURER_URL,
|
||||
ATTR_UPNP_SERIAL,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_HASSIO, SOURCE_SSDP, SOURCE_USER
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_HASSIO,
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_SSDP,
|
||||
SOURCE_USER,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
@ -47,7 +52,7 @@ async def test_flow_discovered_bridges(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -88,7 +93,7 @@ async def test_flow_manual_configuration_decision(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
@ -140,7 +145,7 @@ async def test_flow_manual_configuration(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -184,7 +189,7 @@ async def test_manual_configuration_after_discovery_timeout(hass, aioclient_mock
|
||||
aioclient_mock.get(pydeconz.utils.URL_DISCOVER, exc=asyncio.TimeoutError)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -197,7 +202,7 @@ async def test_manual_configuration_after_discovery_ResponseError(hass, aioclien
|
||||
aioclient_mock.get(pydeconz.utils.URL_DISCOVER, exc=pydeconz.errors.ResponseError)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -216,7 +221,7 @@ async def test_manual_configuration_update_configuration(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -262,7 +267,7 @@ async def test_manual_configuration_dont_update_configuration(hass, aioclient_mo
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -305,7 +310,7 @@ async def test_manual_configuration_timeout_get_bridge(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
@ -346,7 +351,7 @@ async def test_link_get_api_key_ResponseError(hass, aioclient_mock):
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
DECONZ_DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
@ -367,10 +372,46 @@ async def test_link_get_api_key_ResponseError(hass, aioclient_mock):
|
||||
assert result["errors"] == {"base": "no_key"}
|
||||
|
||||
|
||||
async def test_reauth_flow_update_configuration(hass, aioclient_mock):
|
||||
"""Verify reauth flow can update gateway API key."""
|
||||
config_entry = await setup_deconz_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DECONZ_DOMAIN,
|
||||
data=config_entry.data,
|
||||
context={"source": SOURCE_REAUTH},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "link"
|
||||
|
||||
new_api_key = "new_key"
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://1.2.3.4:80/api",
|
||||
json=[{"success": {"username": new_api_key}}],
|
||||
headers={"content-type": CONTENT_TYPE_JSON},
|
||||
)
|
||||
|
||||
aioclient_mock.get(
|
||||
f"http://1.2.3.4:80/api/{new_api_key}/config",
|
||||
json={"bridgeid": BRIDGEID},
|
||||
headers={"content-type": CONTENT_TYPE_JSON},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert config_entry.data[CONF_API_KEY] == new_api_key
|
||||
|
||||
|
||||
async def test_flow_ssdp_discovery(hass, aioclient_mock):
|
||||
"""Test that config flow for one discovered bridge works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
ATTR_SSDP_LOCATION: "http://1.2.3.4:80/",
|
||||
ATTR_UPNP_MANUFACTURER_URL: DECONZ_MANUFACTURERURL,
|
||||
@ -410,7 +451,7 @@ async def test_ssdp_discovery_update_configuration(hass):
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
ATTR_SSDP_LOCATION: "http://2.3.4.5:80/",
|
||||
ATTR_UPNP_MANUFACTURER_URL: DECONZ_MANUFACTURERURL,
|
||||
@ -431,7 +472,7 @@ async def test_ssdp_discovery_dont_update_configuration(hass):
|
||||
config_entry = await setup_deconz_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
ATTR_SSDP_LOCATION: "http://1.2.3.4:80/",
|
||||
ATTR_UPNP_MANUFACTURER_URL: DECONZ_MANUFACTURERURL,
|
||||
@ -450,7 +491,7 @@ async def test_ssdp_discovery_dont_update_existing_hassio_configuration(hass):
|
||||
config_entry = await setup_deconz_integration(hass, source=SOURCE_HASSIO)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
ATTR_SSDP_LOCATION: "http://1.2.3.4:80/",
|
||||
ATTR_UPNP_MANUFACTURER_URL: DECONZ_MANUFACTURERURL,
|
||||
@ -467,7 +508,7 @@ async def test_ssdp_discovery_dont_update_existing_hassio_configuration(hass):
|
||||
async def test_flow_hassio_discovery(hass):
|
||||
"""Test hassio discovery flow works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
"addon": "Mock Addon",
|
||||
CONF_HOST: "mock-deconz",
|
||||
@ -511,7 +552,7 @@ async def test_hassio_discovery_update_configuration(hass):
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "2.3.4.5",
|
||||
CONF_PORT: 8080,
|
||||
@ -535,7 +576,7 @@ async def test_hassio_discovery_dont_update_configuration(hass):
|
||||
await setup_deconz_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
DECONZ_DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: 80,
|
||||
|
@ -181,6 +181,18 @@ async def test_update_address(hass):
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_gateway_trigger_reauth_flow(hass):
|
||||
"""Failed authentication trigger a reauthentication flow."""
|
||||
with patch(
|
||||
"homeassistant.components.deconz.gateway.get_gateway",
|
||||
side_effect=AuthenticationRequired,
|
||||
), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init:
|
||||
await setup_deconz_integration(hass)
|
||||
mock_flow_init.assert_called_once()
|
||||
|
||||
assert hass.data[DECONZ_DOMAIN] == {}
|
||||
|
||||
|
||||
async def test_reset_after_successful_setup(hass):
|
||||
"""Make sure that connection status triggers a dispatcher send."""
|
||||
config_entry = await setup_deconz_integration(hass)
|
||||
|
Loading…
x
Reference in New Issue
Block a user