mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add DHCP discovery support to Bond (#142372)
* Add DHCP discovery support to Bond * fixes * unique ids are always upper * raise_on_progress=False for user * Update tests/components/bond/test_config_flow.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * assert unique id --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
0a7b4d18dc
commit
dcef86a30d
@ -16,6 +16,7 @@ from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .const import DOMAIN
|
||||
@ -91,11 +92,22 @@ class BondConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
self._discovered[CONF_ACCESS_TOKEN] = token
|
||||
try:
|
||||
_, hub_name = await _validate_input(self.hass, self._discovered)
|
||||
bond_id, hub_name = await _validate_input(self.hass, self._discovered)
|
||||
except InputValidationError:
|
||||
return
|
||||
await self.async_set_unique_id(bond_id)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
|
||||
self._discovered[CONF_NAME] = hub_name
|
||||
|
||||
async def async_step_dhcp(
|
||||
self, discovery_info: DhcpServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by dhcp discovery."""
|
||||
host = discovery_info.ip
|
||||
bond_id = discovery_info.hostname.partition("-")[2].upper()
|
||||
await self.async_set_unique_id(bond_id)
|
||||
return await self.async_step_any_discovery(bond_id, host)
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: ZeroconfServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
@ -104,11 +116,17 @@ class BondConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
host: str = discovery_info.host
|
||||
bond_id = name.partition(".")[0]
|
||||
await self.async_set_unique_id(bond_id)
|
||||
return await self.async_step_any_discovery(bond_id, host)
|
||||
|
||||
async def async_step_any_discovery(
|
||||
self, bond_id: str, host: str
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by discovery."""
|
||||
for entry in self._async_current_entries():
|
||||
if entry.unique_id != bond_id:
|
||||
continue
|
||||
updates = {CONF_HOST: host}
|
||||
if entry.state == ConfigEntryState.SETUP_ERROR and (
|
||||
if entry.state is ConfigEntryState.SETUP_ERROR and (
|
||||
token := await async_get_token(self.hass, host)
|
||||
):
|
||||
updates[CONF_ACCESS_TOKEN] = token
|
||||
@ -153,10 +171,14 @@ class BondConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
CONF_HOST: self._discovered[CONF_HOST],
|
||||
}
|
||||
try:
|
||||
_, hub_name = await _validate_input(self.hass, data)
|
||||
bond_id, hub_name = await _validate_input(self.hass, data)
|
||||
except InputValidationError as error:
|
||||
errors["base"] = error.base
|
||||
else:
|
||||
await self.async_set_unique_id(bond_id)
|
||||
self._abort_if_unique_id_configured(
|
||||
updates={CONF_HOST: self._discovered[CONF_HOST]}
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=hub_name,
|
||||
data=data,
|
||||
@ -185,8 +207,10 @@ class BondConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
except InputValidationError as error:
|
||||
errors["base"] = error.base
|
||||
else:
|
||||
await self.async_set_unique_id(bond_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
await self.async_set_unique_id(bond_id, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured(
|
||||
updates={CONF_HOST: user_input[CONF_HOST]}
|
||||
)
|
||||
return self.async_create_entry(title=hub_name, data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
|
@ -3,6 +3,16 @@
|
||||
"name": "Bond",
|
||||
"codeowners": ["@bdraco", "@prystupa", "@joshs85", "@marciogranzotto"],
|
||||
"config_flow": true,
|
||||
"dhcp": [
|
||||
{
|
||||
"hostname": "bond-*",
|
||||
"macaddress": "3C6A2C1*"
|
||||
},
|
||||
{
|
||||
"hostname": "bond-*",
|
||||
"macaddress": "F44E38*"
|
||||
}
|
||||
],
|
||||
"documentation": "https://www.home-assistant.io/integrations/bond",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["bond_async"],
|
||||
|
10
homeassistant/generated/dhcp.py
generated
10
homeassistant/generated/dhcp.py
generated
@ -84,6 +84,16 @@ DHCP: Final[list[dict[str, str | bool]]] = [
|
||||
"hostname": "blink*",
|
||||
"macaddress": "20A171*",
|
||||
},
|
||||
{
|
||||
"domain": "bond",
|
||||
"hostname": "bond-*",
|
||||
"macaddress": "3C6A2C1*",
|
||||
},
|
||||
{
|
||||
"domain": "bond",
|
||||
"hostname": "bond-*",
|
||||
"macaddress": "F44E38*",
|
||||
},
|
||||
{
|
||||
"domain": "broadlink",
|
||||
"registered_devices": True,
|
||||
|
@ -15,6 +15,8 @@ from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .common import (
|
||||
@ -63,6 +65,59 @@ async def test_user_form(hass: HomeAssistant) -> None:
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_user_form_can_create_when_already_discovered(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test we get the user initiated form can create when already discovered."""
|
||||
|
||||
with patch_bond_version(), patch_bond_token():
|
||||
zc_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=ZeroconfServiceInfo(
|
||||
ip_address=ip_address("127.0.0.1"),
|
||||
ip_addresses=[ip_address("127.0.0.1")],
|
||||
hostname="mock_hostname",
|
||||
name="ZXXX12345.some-other-tail-info",
|
||||
port=None,
|
||||
properties={},
|
||||
type="mock_type",
|
||||
),
|
||||
)
|
||||
assert zc_result["type"] is FlowResultType.FORM
|
||||
assert zc_result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with (
|
||||
patch_bond_version(return_value={"bondid": "ZXXX12345"}),
|
||||
patch_bond_device_ids(return_value=["f6776c11", "f6776c12"]),
|
||||
patch_bond_bridge(),
|
||||
patch_bond_device_properties(),
|
||||
patch_bond_device(),
|
||||
patch_bond_device_state(),
|
||||
_patch_async_setup_entry() as mock_setup_entry,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "bond-name"
|
||||
assert result2["data"] == {
|
||||
CONF_HOST: "some host",
|
||||
CONF_ACCESS_TOKEN: "test-token",
|
||||
}
|
||||
assert result2["result"].unique_id == "ZXXX12345"
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_user_form_with_non_bridge(hass: HomeAssistant) -> None:
|
||||
"""Test setup a smart by bond fan."""
|
||||
|
||||
@ -97,6 +152,7 @@ async def test_user_form_with_non_bridge(hass: HomeAssistant) -> None:
|
||||
CONF_HOST: "some host",
|
||||
CONF_ACCESS_TOKEN: "test-token",
|
||||
}
|
||||
assert result2["result"].unique_id == "KXXX12345"
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@ -253,6 +309,107 @@ async def test_zeroconf_form(hass: HomeAssistant) -> None:
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_dhcp_discovery(hass: HomeAssistant) -> None:
|
||||
"""Test DHCP discovery."""
|
||||
|
||||
with patch_bond_version(), patch_bond_token():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_DHCP},
|
||||
data=DhcpServiceInfo(
|
||||
ip="127.0.0.1",
|
||||
hostname="Bond-KVPRBDJ45842",
|
||||
macaddress=format_mac("3c:6a:2c:1c:8c:80"),
|
||||
),
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with (
|
||||
patch_bond_version(return_value={"bondid": "KVPRBDJ45842"}),
|
||||
patch_bond_bridge(),
|
||||
patch_bond_device_ids(),
|
||||
_patch_async_setup_entry() as mock_setup_entry,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_ACCESS_TOKEN: "test-token"},
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "bond-name"
|
||||
assert result2["data"] == {
|
||||
CONF_HOST: "127.0.0.1",
|
||||
CONF_ACCESS_TOKEN: "test-token",
|
||||
}
|
||||
assert result2["result"].unique_id == "KVPRBDJ45842"
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_dhcp_discovery_already_exists(hass: HomeAssistant) -> None:
|
||||
"""Test DHCP discovery for an already existing entry."""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="KVPRBDJ45842",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
patch_bond_version(return_value={"bondid": "KVPRBDJ45842"}),
|
||||
patch_bond_token(),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_DHCP},
|
||||
data=DhcpServiceInfo(
|
||||
ip="127.0.0.1",
|
||||
hostname="Bond-KVPRBDJ45842".lower(),
|
||||
macaddress=format_mac("3c:6a:2c:1c:8c:80"),
|
||||
),
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_dhcp_discovery_short_name(hass: HomeAssistant) -> None:
|
||||
"""Test DHCP discovery with the name cut off."""
|
||||
|
||||
with patch_bond_version(), patch_bond_token():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_DHCP},
|
||||
data=DhcpServiceInfo(
|
||||
ip="127.0.0.1",
|
||||
hostname="Bond-KVPRBDJ",
|
||||
macaddress=format_mac("3c:6a:2c:1c:8c:80"),
|
||||
),
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with (
|
||||
patch_bond_version(return_value={"bondid": "KVPRBDJ45842"}),
|
||||
patch_bond_bridge(),
|
||||
patch_bond_device_ids(),
|
||||
_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"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "bond-name"
|
||||
assert result2["data"] == {
|
||||
CONF_HOST: "127.0.0.1",
|
||||
CONF_ACCESS_TOKEN: "test-token",
|
||||
}
|
||||
assert result2["result"].unique_id == "KVPRBDJ45842"
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_zeroconf_form_token_unavailable(hass: HomeAssistant) -> None:
|
||||
"""Test we get the discovery form and we handle the token being unavailable."""
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user