Add MAC connection through DHCP discovery to Home Connect devices (#144611)

* Add MAC connection through DHCP discovery to Home Connect devices

* Update snapshots
This commit is contained in:
J. Diego Rodríguez Royo 2025-05-12 21:11:12 +02:00 committed by GitHub
parent b5445c0061
commit 15a4514c7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 123 additions and 50 deletions

View File

@ -8,7 +8,8 @@ import jwt
import voluptuous as vol
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers import config_entry_oauth2_flow, device_registry as dr
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import DOMAIN
@ -58,3 +59,22 @@ class OAuth2FlowHandler(
)
self._abort_if_unique_id_configured()
return await super().async_oauth_create_entry(data)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle a DHCP discovery."""
device_registry = dr.async_get(self.hass)
if device_entry := device_registry.async_get_device(
identifiers={
(DOMAIN, discovery_info.hostname),
(DOMAIN, discovery_info.hostname.split("-")[-1]),
}
):
device_registry.async_update_device(
device_entry.id,
new_connections={
(dr.CONNECTION_NETWORK_MAC, discovery_info.macaddress)
},
)
return await super().async_step_dhcp(discovery_info)

View File

@ -97,7 +97,7 @@
"connected": true,
"type": "Hob",
"enumber": "HCS000000/05",
"haId": "BOSCH-HCS000000-D00000000005"
"haId": "BOSCH-HCS000000-68A40E000000"
},
{
"name": "CookProcessor",
@ -106,7 +106,7 @@
"connected": true,
"type": "CookProcessor",
"enumber": "HCS000000/06",
"haId": "BOSCH-HCS000000-D00000000006"
"haId": "123456789012345678"
},
{
"name": "DNE",

View File

@ -1,6 +1,26 @@
# serializer version: 1
# name: test_async_get_config_entry_diagnostics
dict({
'123456789012345678': dict({
'brand': 'BOSCH',
'connected': True,
'e_number': 'HCS000000/06',
'ha_id': '123456789012345678',
'name': 'CookProcessor',
'programs': list([
]),
'settings': dict({
}),
'status': dict({
'BSH.Common.Status.DoorState': 'BSH.Common.EnumType.DoorState.Closed',
'BSH.Common.Status.OperationState': 'BSH.Common.EnumType.OperationState.Ready',
'BSH.Common.Status.RemoteControlActive': True,
'BSH.Common.Status.RemoteControlStartAllowed': True,
'Refrigeration.Common.Status.Door.Refrigerator': 'BSH.Common.EnumType.DoorState.Open',
}),
'type': 'CookProcessor',
'vib': 'HCS000006',
}),
'BOSCH-000000000-000000000000': dict({
'brand': 'BOSCH',
'connected': True,
@ -21,6 +41,26 @@
'type': 'DNE',
'vib': 'HCS000000',
}),
'BOSCH-HCS000000-68A40E000000': dict({
'brand': 'BOSCH',
'connected': True,
'e_number': 'HCS000000/05',
'ha_id': 'BOSCH-HCS000000-68A40E000000',
'name': 'Hob',
'programs': list([
]),
'settings': dict({
}),
'status': dict({
'BSH.Common.Status.DoorState': 'BSH.Common.EnumType.DoorState.Closed',
'BSH.Common.Status.OperationState': 'BSH.Common.EnumType.OperationState.Ready',
'BSH.Common.Status.RemoteControlActive': True,
'BSH.Common.Status.RemoteControlStartAllowed': True,
'Refrigeration.Common.Status.Door.Refrigerator': 'BSH.Common.EnumType.DoorState.Open',
}),
'type': 'Hob',
'vib': 'HCS000005',
}),
'BOSCH-HCS000000-D00000000001': dict({
'brand': 'BOSCH',
'connected': True,
@ -114,46 +154,6 @@
'type': 'Hood',
'vib': 'HCS000004',
}),
'BOSCH-HCS000000-D00000000005': dict({
'brand': 'BOSCH',
'connected': True,
'e_number': 'HCS000000/05',
'ha_id': 'BOSCH-HCS000000-D00000000005',
'name': 'Hob',
'programs': list([
]),
'settings': dict({
}),
'status': dict({
'BSH.Common.Status.DoorState': 'BSH.Common.EnumType.DoorState.Closed',
'BSH.Common.Status.OperationState': 'BSH.Common.EnumType.OperationState.Ready',
'BSH.Common.Status.RemoteControlActive': True,
'BSH.Common.Status.RemoteControlStartAllowed': True,
'Refrigeration.Common.Status.Door.Refrigerator': 'BSH.Common.EnumType.DoorState.Open',
}),
'type': 'Hob',
'vib': 'HCS000005',
}),
'BOSCH-HCS000000-D00000000006': dict({
'brand': 'BOSCH',
'connected': True,
'e_number': 'HCS000000/06',
'ha_id': 'BOSCH-HCS000000-D00000000006',
'name': 'CookProcessor',
'programs': list([
]),
'settings': dict({
}),
'status': dict({
'BSH.Common.Status.DoorState': 'BSH.Common.EnumType.DoorState.Closed',
'BSH.Common.Status.OperationState': 'BSH.Common.EnumType.OperationState.Ready',
'BSH.Common.Status.RemoteControlActive': True,
'BSH.Common.Status.RemoteControlStartAllowed': True,
'Refrigeration.Common.Status.Door.Refrigerator': 'BSH.Common.EnumType.DoorState.Open',
}),
'type': 'CookProcessor',
'vib': 'HCS000006',
}),
'BOSCH-HCS01OVN1-43E0065FE245': dict({
'brand': 'BOSCH',
'connected': True,

View File

@ -1,9 +1,11 @@
"""Test the Home Connect config flow."""
from collections.abc import Awaitable, Callable
from http import HTTPStatus
from unittest.mock import patch
from unittest.mock import MagicMock, patch
from aiohomeconnect.const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
from aiohomeconnect.model import HomeAppliance
import pytest
from homeassistant import config_entries, setup
@ -11,7 +13,7 @@ from homeassistant.components.home_connect.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers import config_entry_oauth2_flow, device_registry as dr
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .conftest import FAKE_ACCESS_TOKEN, FAKE_REFRESH_TOKEN
@ -337,17 +339,17 @@ async def test_zeroconf_flow_already_setup(
@pytest.mark.usefixtures("current_request_with_host")
@pytest.mark.parametrize("dchp_discovery", DHCP_DISCOVERY)
@pytest.mark.parametrize("dhcp_discovery", DHCP_DISCOVERY)
async def test_dhcp_flow(
hass: HomeAssistant,
hass_client_no_auth: ClientSessionGenerator,
aioclient_mock: AiohttpClientMocker,
dchp_discovery: DhcpServiceInfo,
dhcp_discovery: DhcpServiceInfo,
) -> None:
"""Test DHCP discovery."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dchp_discovery
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dhcp_discovery
)
state = config_entry_oauth2_flow._encode_jwt(
hass,
@ -391,8 +393,6 @@ async def test_dhcp_flow(
@pytest.mark.usefixtures("current_request_with_host")
async def test_dhcp_flow_already_setup(
hass: HomeAssistant,
hass_client_no_auth: ClientSessionGenerator,
aioclient_mock: AiohttpClientMocker,
config_entry: MockConfigEntry,
) -> None:
"""Test DHCP discovery with already setup device."""
@ -403,3 +403,56 @@ async def test_dhcp_flow_already_setup(
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.usefixtures("current_request_with_host")
@pytest.mark.parametrize(
("dhcp_discovery", "appliance"),
[
(
DhcpServiceInfo(
ip="1.1.1.1",
hostname="bosch-cookprocessor-123456789012345678",
macaddress="c8:d7:78:00:00:00",
),
"CookProcessor",
),
(
DhcpServiceInfo(
ip="1.1.1.1",
hostname="BOSCH-HCS000000-68A40E000000",
macaddress="68:a4:0e:00:00:00",
),
"Hob",
),
],
indirect=["appliance"],
)
async def test_dhcp_flow_complete_device_information(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
client: MagicMock,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
dhcp_discovery: DhcpServiceInfo,
appliance: HomeAppliance,
) -> None:
"""Test DHCP discovery with complete device information."""
assert await integration_setup(client)
assert config_entry.state is ConfigEntryState.LOADED
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
assert device.connections == set()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dhcp_discovery
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
assert device.connections == {
(dr.CONNECTION_NETWORK_MAC, dhcp_discovery.macaddress)
}