Use dhcp discovery to update midea device IP (#180955)

This commit is contained in:
Lucas Mindêllo de Andrade
2026-09-02 16:57:55 +02:00
committed by GitHub
parent 59064e3be4
commit 00d7c71f50
7 changed files with 126 additions and 3 deletions
@@ -31,7 +31,9 @@ from homeassistant.const import (
CONF_TYPE,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import (
CONF_ACCOUNT,
@@ -772,3 +774,29 @@ class MideaConfigFlow(ConfigFlow, domain=DOMAIN):
data_schema=schema,
errors={"base": error} if error else None,
)
@override
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery of a known Midea device.
Only devices already configured (matched via ``registered_devices``)
reach this step. It is used to keep the stored host in sync with the
current IP address of the device.
"""
mac = format_mac(discovery_info.macaddress)
for entry in self._async_current_entries():
if (entry_mac := entry.data.get(CONF_MAC)) is None or format_mac(
entry_mac
) != mac:
continue
if entry.data[CONF_IP_ADDRESS] != discovery_info.ip:
self.hass.config_entries.async_update_entry(
entry,
data=entry.data | {CONF_IP_ADDRESS: discovery_info.ip},
)
self.hass.config_entries.async_schedule_reload(entry.entry_id)
return self.async_abort(reason="already_configured")
return self.async_abort(reason="no_devices_found")
@@ -3,6 +3,11 @@
"name": "Midea",
"codeowners": ["@chemelli74", "@rokam", "@caibinqing"],
"config_flow": true,
"dhcp": [
{
"registered_devices": true
}
],
"documentation": "https://www.home-assistant.io/integrations/midea",
"integration_type": "device",
"iot_class": "local_polling",
+4
View File
@@ -651,6 +651,10 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"hostname": "lyric-*",
"macaddress": "00D02D*",
},
{
"domain": "midea",
"registered_devices": True,
},
{
"domain": "mitsubishi_comfort",
"registered_devices": True,
+4 -2
View File
@@ -7,9 +7,9 @@ from unittest.mock import AsyncMock, patch
from midealocal.const import DeviceType
import pytest
from homeassistant.components.midea.const import CONF_KEY, CONF_SUBTYPE, DOMAIN
from homeassistant.components.midea.const import CONF_KEY, CONF_SN, CONF_SUBTYPE, DOMAIN
from homeassistant.components.midea.device_catalog import MIDEA_DEVICE_NAMES
from homeassistant.const import CONF_NAME, CONF_TOKEN, CONF_TYPE
from homeassistant.const import CONF_MAC, CONF_NAME, CONF_TOKEN, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@@ -179,6 +179,8 @@ def mock_config_entry() -> Callable[[DummyDevice], MockConfigEntry]:
**BASE_DATA,
CONF_TYPE: device.device_type,
CONF_NAME: MIDEA_DEVICE_NAMES[device.device_type],
CONF_MAC: TEST_MAC_ADDRESS,
CONF_SN: TEST_SERIAL_NUMBER,
CONF_TOKEN: TEST_TOKEN,
CONF_KEY: TEST_KEY,
CONF_SUBTYPE: TEST_SUBTYPE,
+1
View File
@@ -15,6 +15,7 @@ from homeassistant.const import (
CONF_TYPE,
)
TEST_HOSTNAME = "net_ac_2233"
TEST_DEVICE_ID = 12345678
TEST_IP_ADDRESS = "1.1.1.1"
TEST_KEY = "bb" * 16
@@ -17,10 +17,12 @@
'device_id': 12345678,
'ip_address': '1.1.1.1',
'key': '**REDACTED**',
'mac': '**REDACTED**',
'model': 'MSAGBU-09HRFN8',
'name': 'Air Conditioner',
'port': 6444,
'protocol': 3,
'sn': '**REDACTED**',
'subtype': 0,
'token': '**REDACTED**',
'type': 172,
+82 -1
View File
@@ -1,5 +1,6 @@
"""Tests for the Midea config flow."""
from collections.abc import Callable
from functools import partial
from unittest.mock import AsyncMock, MagicMock, patch
@@ -22,7 +23,7 @@ from homeassistant.components.midea.const import (
DOMAIN,
)
from homeassistant.components.midea.device_catalog import MIDEA_DEVICE_NAMES
from homeassistant.config_entries import SOURCE_USER
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER
from homeassistant.const import (
CONF_DEVICE,
CONF_DEVICE_ID,
@@ -33,17 +34,21 @@ from homeassistant.const import (
CONF_PASSWORD,
CONF_PORT,
CONF_PROTOCOL,
CONF_SOURCE,
CONF_TOKEN,
CONF_TYPE,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .conftest import DummyDevice, default_ac_device
from .const import (
BASE_DATA,
DISCOVERY_RESULT,
EXTENDED_DATA,
TEST_DEVICE_ID,
TEST_HOSTNAME,
TEST_IP_ADDRESS,
TEST_KEY,
TEST_MAC_ADDRESS,
@@ -2031,3 +2036,79 @@ async def test_auth_method_preset_login_failed(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "auth_method"
assert result["errors"] == {"base": "preset_login_failed"}
async def test_dhcp_discovery_updates_host(
hass: HomeAssistant,
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
) -> None:
"""Test DHCP discovery of a known device updates its stored host."""
config_entry = mock_config_entry(default_ac_device())
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_DHCP},
data=DhcpServiceInfo(
hostname=TEST_HOSTNAME,
ip="127.0.0.42",
macaddress=TEST_MAC_ADDRESS.replace(":", ""),
),
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert config_entry.data[CONF_IP_ADDRESS] == "127.0.0.42"
async def test_dhcp_discovery_same_host(
hass: HomeAssistant,
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
) -> None:
"""Test DHCP discovery does nothing when the host is already up to date."""
config_entry = mock_config_entry(default_ac_device())
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_DHCP},
data=DhcpServiceInfo(
hostname=TEST_HOSTNAME,
ip=TEST_IP_ADDRESS,
macaddress=TEST_MAC_ADDRESS.replace(":", ""),
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert config_entry.data[CONF_IP_ADDRESS] == TEST_IP_ADDRESS
async def test_dhcp_discovery_no_match(
hass: HomeAssistant,
mock_config_entry: Callable[[DummyDevice], MockConfigEntry],
) -> None:
"""Test DHCP discovery aborts when no matching entry is configured."""
config_entry = mock_config_entry(default_ac_device())
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_DHCP},
data=DhcpServiceInfo(
hostname=TEST_HOSTNAME,
ip="1.2.3.4",
macaddress="aabbccddeeff",
),
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_devices_found"
assert config_entry.data[CONF_IP_ADDRESS] == TEST_IP_ADDRESS