Avoid having to ask for the bond token when possible during config (#46845)
This commit is contained in:
parent
00dd557cce
commit
d68a51ddce
9 changed files with 166 additions and 34 deletions
|
@ -14,16 +14,17 @@ from homeassistant.const import (
|
|||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
from .const import CONF_BOND_ID
|
||||
from .const import DOMAIN # pylint:disable=unused-import
|
||||
from .utils import BondHub
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA_USER = vol.Schema(
|
||||
|
||||
USER_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_HOST): str, vol.Required(CONF_ACCESS_TOKEN): str}
|
||||
)
|
||||
DATA_SCHEMA_DISCOVERY = vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str})
|
||||
DISCOVERY_SCHEMA = vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str})
|
||||
TOKEN_SCHEMA = vol.Schema({})
|
||||
|
||||
|
||||
async def _validate_input(data: Dict[str, Any]) -> Tuple[str, Optional[str]]:
|
||||
|
@ -56,7 +57,30 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||
|
||||
_discovered: dict = None
|
||||
def __init__(self):
|
||||
"""Initialize config flow."""
|
||||
self._discovered: dict = None
|
||||
|
||||
async def _async_try_automatic_configure(self):
|
||||
"""Try to auto configure the device.
|
||||
|
||||
Failure is acceptable here since the device may have been
|
||||
online longer then the allowed setup period, and we will
|
||||
instead ask them to manually enter the token.
|
||||
"""
|
||||
bond = Bond(self._discovered[CONF_HOST], "")
|
||||
try:
|
||||
response = await bond.token()
|
||||
except ClientConnectionError:
|
||||
return
|
||||
|
||||
token = response.get("token")
|
||||
if token is None:
|
||||
return
|
||||
|
||||
self._discovered[CONF_ACCESS_TOKEN] = token
|
||||
_, hub_name = await _validate_input(self._discovered)
|
||||
self._discovered[CONF_NAME] = hub_name
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: Optional[Dict[str, Any]] = None
|
||||
|
@ -68,11 +92,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
await self.async_set_unique_id(bond_id)
|
||||
self._abort_if_unique_id_configured({CONF_HOST: host})
|
||||
|
||||
self._discovered = {
|
||||
CONF_HOST: host,
|
||||
CONF_BOND_ID: bond_id,
|
||||
self._discovered = {CONF_HOST: host, CONF_NAME: bond_id}
|
||||
await self._async_try_automatic_configure()
|
||||
|
||||
self.context.update(
|
||||
{
|
||||
"title_placeholders": {
|
||||
CONF_HOST: self._discovered[CONF_HOST],
|
||||
CONF_NAME: self._discovered[CONF_NAME],
|
||||
}
|
||||
self.context.update({"title_placeholders": self._discovered})
|
||||
}
|
||||
)
|
||||
|
||||
return await self.async_step_confirm()
|
||||
|
||||
|
@ -82,16 +112,37 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
"""Handle confirmation flow for discovered bond hub."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
data = user_input.copy()
|
||||
data[CONF_HOST] = self._discovered[CONF_HOST]
|
||||
if CONF_ACCESS_TOKEN in self._discovered:
|
||||
return self.async_create_entry(
|
||||
title=self._discovered[CONF_NAME],
|
||||
data={
|
||||
CONF_ACCESS_TOKEN: self._discovered[CONF_ACCESS_TOKEN],
|
||||
CONF_HOST: self._discovered[CONF_HOST],
|
||||
},
|
||||
)
|
||||
|
||||
data = {
|
||||
CONF_ACCESS_TOKEN: user_input[CONF_ACCESS_TOKEN],
|
||||
CONF_HOST: self._discovered[CONF_HOST],
|
||||
}
|
||||
try:
|
||||
return await self._try_create_entry(data)
|
||||
_, hub_name = await _validate_input(data)
|
||||
except InputValidationError as error:
|
||||
errors["base"] = error.base
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=hub_name,
|
||||
data=data,
|
||||
)
|
||||
|
||||
if CONF_ACCESS_TOKEN in self._discovered:
|
||||
data_schema = TOKEN_SCHEMA
|
||||
else:
|
||||
data_schema = DISCOVERY_SCHEMA
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="confirm",
|
||||
data_schema=DATA_SCHEMA_DISCOVERY,
|
||||
data_schema=data_schema,
|
||||
errors=errors,
|
||||
description_placeholders=self._discovered,
|
||||
)
|
||||
|
@ -103,20 +154,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
errors = {}
|
||||
if user_input is not None:
|
||||
try:
|
||||
return await self._try_create_entry(user_input)
|
||||
bond_id, hub_name = await _validate_input(user_input)
|
||||
except InputValidationError as error:
|
||||
errors["base"] = error.base
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA_USER, errors=errors
|
||||
)
|
||||
|
||||
async def _try_create_entry(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
bond_id, name = await _validate_input(data)
|
||||
else:
|
||||
await self.async_set_unique_id(bond_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
hub_name = name or bond_id
|
||||
return self.async_create_entry(title=hub_name, data=data)
|
||||
return self.async_create_entry(title=hub_name, data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=USER_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
|
||||
class InputValidationError(exceptions.HomeAssistantError):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Bond",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/bond",
|
||||
"requirements": ["bond-api==0.1.10"],
|
||||
"requirements": ["bond-api==0.1.11"],
|
||||
"zeroconf": ["_bond._tcp.local."],
|
||||
"codeowners": ["@prystupa"],
|
||||
"quality_scale": "platinum"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"config": {
|
||||
"flow_title": "Bond: {bond_id} ({host})",
|
||||
"flow_title": "Bond: {name} ({host})",
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "Do you want to set up {bond_id}?",
|
||||
"description": "Do you want to set up {name}?",
|
||||
"data": {
|
||||
"access_token": "[%key:common::config_flow::data::access_token%]"
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
"old_firmware": "Unsupported old firmware on the Bond device - please upgrade before continuing",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"flow_title": "Bond: {bond_id} ({host})",
|
||||
"flow_title": "Bond: {name} ({host})",
|
||||
"step": {
|
||||
"confirm": {
|
||||
"data": {
|
||||
"access_token": "Access Token"
|
||||
},
|
||||
"description": "Do you want to set up {bond_id}?"
|
||||
"description": "Do you want to set up {name}?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
|
|
@ -150,11 +150,11 @@ class BondHub:
|
|||
return self._version.get("make", BRIDGE_MAKE)
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
def name(self) -> str:
|
||||
"""Get the name of this bridge."""
|
||||
if not self.is_bridge and self._devices:
|
||||
return self._devices[0].name
|
||||
return self._bridge.get("name")
|
||||
return self._bridge["name"]
|
||||
|
||||
@property
|
||||
def location(self) -> Optional[str]:
|
||||
|
|
|
@ -371,7 +371,7 @@ blockchain==1.4.4
|
|||
# bme680==1.0.5
|
||||
|
||||
# homeassistant.components.bond
|
||||
bond-api==0.1.10
|
||||
bond-api==0.1.11
|
||||
|
||||
# homeassistant.components.amazon_polly
|
||||
# homeassistant.components.route53
|
||||
|
|
|
@ -205,7 +205,7 @@ blebox_uniapi==1.3.2
|
|||
blinkpy==0.17.0
|
||||
|
||||
# homeassistant.components.bond
|
||||
bond-api==0.1.10
|
||||
bond-api==0.1.11
|
||||
|
||||
# homeassistant.components.braviatv
|
||||
bravia-tv==1.0.8
|
||||
|
|
|
@ -30,12 +30,13 @@ async def setup_bond_entity(
|
|||
patch_device_ids=False,
|
||||
patch_platforms=False,
|
||||
patch_bridge=False,
|
||||
patch_token=False,
|
||||
):
|
||||
"""Set up Bond entity."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch_start_bpup(), patch_bond_bridge(
|
||||
enabled=patch_bridge
|
||||
with patch_start_bpup(), patch_bond_bridge(enabled=patch_bridge), patch_bond_token(
|
||||
enabled=patch_token
|
||||
), patch_bond_version(enabled=patch_version), patch_bond_device_ids(
|
||||
enabled=patch_device_ids
|
||||
), patch_setup_entry(
|
||||
|
@ -60,6 +61,7 @@ async def setup_platform(
|
|||
props: Dict[str, Any] = None,
|
||||
state: Dict[str, Any] = None,
|
||||
bridge: Dict[str, Any] = None,
|
||||
token: Dict[str, Any] = None,
|
||||
):
|
||||
"""Set up the specified Bond platform."""
|
||||
mock_entry = MockConfigEntry(
|
||||
|
@ -71,7 +73,7 @@ async def setup_platform(
|
|||
with patch("homeassistant.components.bond.PLATFORMS", [platform]):
|
||||
with patch_bond_version(return_value=bond_version), patch_bond_bridge(
|
||||
return_value=bridge
|
||||
), patch_bond_device_ids(
|
||||
), patch_bond_token(return_value=token), patch_bond_device_ids(
|
||||
return_value=[bond_device_id]
|
||||
), patch_start_bpup(), patch_bond_device(
|
||||
return_value=discovered_device
|
||||
|
@ -124,6 +126,23 @@ def patch_bond_bridge(
|
|||
)
|
||||
|
||||
|
||||
def patch_bond_token(
|
||||
enabled: bool = True, return_value: Optional[dict] = None, side_effect=None
|
||||
):
|
||||
"""Patch Bond API token endpoint."""
|
||||
if not enabled:
|
||||
return nullcontext()
|
||||
|
||||
if return_value is None:
|
||||
return_value = {"locked": 1}
|
||||
|
||||
return patch(
|
||||
"homeassistant.components.bond.Bond.token",
|
||||
return_value=return_value,
|
||||
side_effect=side_effect,
|
||||
)
|
||||
|
||||
|
||||
def patch_bond_device_ids(enabled: bool = True, return_value=None, side_effect=None):
|
||||
"""Patch Bond API devices endpoint."""
|
||||
if not enabled:
|
||||
|
|
|
@ -13,6 +13,7 @@ from .common import (
|
|||
patch_bond_device,
|
||||
patch_bond_device_ids,
|
||||
patch_bond_device_properties,
|
||||
patch_bond_token,
|
||||
patch_bond_version,
|
||||
)
|
||||
|
||||
|
@ -221,6 +222,70 @@ async def test_zeroconf_form(hass: core.HomeAssistant):
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_zeroconf_form_token_unavailable(hass: core.HomeAssistant):
|
||||
"""Test we get the discovery form and we handle the token being unavailable."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
with patch_bond_version(), patch_bond_token():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data={"name": "test-bond-id.some-other-tail-info", "host": "test-host"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch_bond_version(), patch_bond_bridge(), patch_bond_device_ids(), _patch_async_setup() as mock_setup, _patch_async_setup_entry() as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_ACCESS_TOKEN: "test-token"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "bond-name"
|
||||
assert result2["data"] == {
|
||||
CONF_HOST: "test-host",
|
||||
CONF_ACCESS_TOKEN: "test-token",
|
||||
}
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_zeroconf_form_with_token_available(hass: core.HomeAssistant):
|
||||
"""Test we get the discovery form when we can get the token."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
with patch_bond_version(return_value={"bondid": "test-bond-id"}), patch_bond_token(
|
||||
return_value={"token": "discovered-token"}
|
||||
), patch_bond_bridge(
|
||||
return_value={"name": "discovered-name"}
|
||||
), patch_bond_device_ids():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data={"name": "test-bond-id.some-other-tail-info", "host": "test-host"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with _patch_async_setup() as mock_setup, _patch_async_setup_entry() as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "discovered-name"
|
||||
assert result2["data"] == {
|
||||
CONF_HOST: "test-host",
|
||||
CONF_ACCESS_TOKEN: "discovered-token",
|
||||
}
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_zeroconf_already_configured(hass: core.HomeAssistant):
|
||||
"""Test starting a flow from discovery when already configured."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
|
|
Loading…
Add table
Reference in a new issue