From 07e8b780a2a139ec64b3a4c6c4b2bc4cf253050a Mon Sep 17 00:00:00 2001 From: cdnninja Date: Wed, 11 Feb 2026 15:27:35 -0700 Subject: [PATCH] Add DHCP Discovery to vesync (#162259) Co-authored-by: Joostlek --- homeassistant/components/vesync/manifest.json | 1 + .../components/vesync/quality_scale.yaml | 6 ++- homeassistant/generated/dhcp.py | 4 ++ tests/components/vesync/test_config_flow.py | 53 ++++++++++++++++++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json index 652b47ebb1c..195a3315341 100644 --- a/homeassistant/components/vesync/manifest.json +++ b/homeassistant/components/vesync/manifest.json @@ -10,6 +10,7 @@ "@sapuseven" ], "config_flow": true, + "dhcp": [{ "hostname": "levoit-*" }], "documentation": "https://www.home-assistant.io/integrations/vesync", "integration_type": "hub", "iot_class": "cloud_polling", diff --git a/homeassistant/components/vesync/quality_scale.yaml b/homeassistant/components/vesync/quality_scale.yaml index 56fe454e7a0..b46a06af0dd 100644 --- a/homeassistant/components/vesync/quality_scale.yaml +++ b/homeassistant/components/vesync/quality_scale.yaml @@ -42,8 +42,10 @@ rules: diagnostics: status: todo comment: room to remove content such as entities. - discovery-update-info: todo - discovery: todo + discovery-update-info: + status: exempt + comment: Devices aren't connected locally, nothing to update + discovery: done docs-data-update: todo docs-examples: todo docs-known-limitations: todo diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 0f975fbab17..e650435a2e0 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -1342,6 +1342,10 @@ DHCP: Final[list[dict[str, str | bool]]] = [ "domain": "verisure", "macaddress": "0023C1*", }, + { + "domain": "vesync", + "hostname": "levoit-*", + }, { "domain": "vicare", "macaddress": "B87424*", diff --git a/tests/components/vesync/test_config_flow.py b/tests/components/vesync/test_config_flow.py index 0ff77e46225..405ff1f9e14 100644 --- a/tests/components/vesync/test_config_flow.py +++ b/tests/components/vesync/test_config_flow.py @@ -5,10 +5,12 @@ from unittest.mock import PropertyMock, patch from pyvesync.utils.errors import VeSyncLoginError from homeassistant.components.vesync import DOMAIN -from homeassistant.config_entries import SOURCE_USER +from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo from tests.common import MockConfigEntry @@ -150,3 +152,52 @@ async def test_reauth_flow_invalid_auth(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.ABORT assert result["reason"] == "reauth_successful" + + +async def test_dhcp_discovery( + hass: HomeAssistant, device_registry: dr.DeviceRegistry +) -> None: + """Test DHCP discovery flow.""" + + service_info = DhcpServiceInfo( + hostname="Levoit-Purifier", + ip="1.2.3.4", + macaddress="aabbccddeeff", + ) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=service_info, + ) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + # Configure the flow to create the config entry + with patch("pyvesync.vesync.VeSync.login"): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "user", CONF_PASSWORD: "pass"}, + ) + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["result"].unique_id == "TESTACCOUNTID" + + +async def test_dhcp_discovery_duplicate( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test DHCP discovery flow with already setup integration.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=DhcpServiceInfo( + hostname="Levoit-Purifier", + ip="1.2.3.4", + macaddress="aabbccddeeff", + ), + ) + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "already_configured"