Use dataclass for DhcpServiceInfo (#60136)

Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
epenet 2021-11-23 13:35:53 +01:00 committed by GitHub
parent e673d9dbd0
commit 560546f65e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 89 additions and 23 deletions

View File

@ -1,12 +1,13 @@
"""The dhcp integration."""
from dataclasses import dataclass
from datetime import timedelta
import fnmatch
from ipaddress import ip_address as make_ip_address
import logging
import os
import threading
from typing import Final, TypedDict
from typing import Any, Final
from aiodiscover import DiscoverHosts
from aiodiscover.discovery import (
@ -32,12 +33,14 @@ from homeassistant.const import (
STATE_HOME,
)
from homeassistant.core import Event, HomeAssistant, State, callback
from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.event import (
async_track_state_added_domain,
async_track_time_interval,
)
from homeassistant.helpers.frame import report
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import async_get_dhcp
from homeassistant.util.async_ import run_callback_threadsafe
@ -55,13 +58,33 @@ SCAN_INTERVAL = timedelta(minutes=60)
_LOGGER = logging.getLogger(__name__)
class DhcpServiceInfo(TypedDict):
@dataclass
class DhcpServiceInfo(BaseServiceInfo):
"""Prepared info from dhcp entries."""
ip: str
ip: str # pylint: disable=invalid-name
hostname: str
macaddress: str
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any:
"""
Allow property access by name for compatibility reason.
Deprecated, and will be removed in version 2022.6.
"""
if not self._warning_logged:
report(
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
exclude_integrations={"dhcp"},
error_if_core=False,
level=logging.DEBUG,
)
self._warning_logged = True
return getattr(self, name)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the dhcp component."""

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import abc
import asyncio
from collections.abc import Iterable, Mapping
from dataclasses import dataclass
from types import MappingProxyType
from typing import Any, TypedDict
import uuid
@ -25,6 +26,11 @@ RESULT_TYPE_SHOW_PROGRESS_DONE = "progress_done"
EVENT_DATA_ENTRY_FLOW_PROGRESSED = "data_entry_flow_progressed"
@dataclass
class BaseServiceInfo:
"""Base class for discovery ServiceInfo."""
class FlowError(HomeAssistantError):
"""Error while configuring an account."""
@ -301,7 +307,7 @@ class FlowManager(abc.ABC):
self,
flow: Any,
step_id: str,
user_input: dict | None,
user_input: dict | BaseServiceInfo | None,
step_done: asyncio.Future | None = None,
) -> FlowResult:
"""Handle a step of a flow."""

View File

@ -51,7 +51,10 @@ class MissingIntegrationFrame(HomeAssistantError):
def report(
what: str, exclude_integrations: set | None = None, error_if_core: bool = True
what: str,
exclude_integrations: set | None = None,
error_if_core: bool = True,
level: int = logging.WARNING,
) -> None:
"""Report incorrect usage.
@ -68,11 +71,13 @@ def report(
_LOGGER.warning(msg, stack_info=True)
return
report_integration(what, integration_frame)
report_integration(what, integration_frame, level)
def report_integration(
what: str, integration_frame: tuple[FrameSummary, str, str]
what: str,
integration_frame: tuple[FrameSummary, str, str],
level: int = logging.WARNING,
) -> None:
"""Report incorrect usage in an integration.
@ -86,7 +91,8 @@ def report_integration(
else:
extra = ""
_LOGGER.warning(
_LOGGER.log(
level,
"Detected integration that %s. "
"Please report issue%s for %s using this method at %s, line %s: %s",
what,

View File

@ -191,7 +191,9 @@ async def test_discovered_dhcp(
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip="1.2.3.4", macaddress=MOCK_MAC_ADDR),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.4", macaddress=MOCK_MAC_ADDR, hostname="mock_hostname"
),
)
assert result["type"] == RESULT_TYPE_FORM
assert result["errors"] == {}
@ -246,7 +248,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result2 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip="1.2.3.4", macaddress=MOCK_MAC_ADDR),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.4", macaddress=MOCK_MAC_ADDR, hostname="mock_hostname"
),
)
assert result2["type"] == RESULT_TYPE_ABORT
assert result2["reason"] == "already_in_progress"
@ -254,7 +258,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result3 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip="1.2.3.4", macaddress="00:00:00:00:00:00"),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.4", macaddress="00:00:00:00:00:00", hostname="mock_hostname"
),
)
assert result3["type"] == RESULT_TYPE_ABORT
assert result3["reason"] == "already_in_progress"

View File

@ -23,7 +23,9 @@ ZEROCONF_DISCOVERY_INFO = zeroconf.ZeroconfServiceInfo(
)
DHCP_DISCOVERY_INFO = dhcp.DhcpServiceInfo(
hostname="Hunter Douglas Powerview Hub", ip="1.2.3.4"
hostname="Hunter Douglas Powerview Hub",
ip="1.2.3.4",
macaddress="AA:BB:CC:DD:EE:FF",
)
DISCOVERY_DATA = [

View File

@ -84,7 +84,9 @@ MOCK_SSDP_DATA_WRONGMODEL = {
ATTR_UPNP_MODEL_NAME: "HW-Qfake",
ATTR_UPNP_UDN: "uuid:0d1cef00-00dc-1000-9c80-4844f7b172df",
}
MOCK_DHCP_DATA = dhcp.DhcpServiceInfo(ip="fake_host", macaddress="aa:bb:cc:dd:ee:ff")
MOCK_DHCP_DATA = dhcp.DhcpServiceInfo(
ip="fake_host", macaddress="aa:bb:cc:dd:ee:ff", hostname="fake_hostname"
)
EXISTING_IP = "192.168.40.221"
MOCK_ZEROCONF_DATA = zeroconf.ZeroconfServiceInfo(
host="fake_host",
@ -1131,7 +1133,9 @@ async def test_update_legacy_missing_mac_from_dhcp(hass, remote: Mock):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff"),
data=dhcp.DhcpServiceInfo(
ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff", hostname="fake_hostname"
),
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
@ -1165,7 +1169,9 @@ async def test_update_legacy_missing_mac_from_dhcp_no_unique_id(hass, remote: Mo
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff"),
data=dhcp.DhcpServiceInfo(
ip=EXISTING_IP, macaddress="aa:bb:cc:dd:ee:ff", hostname="fake_hostname"
),
)
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1

View File

@ -141,6 +141,7 @@ async def test_dhcp(hass):
data=dhcp.DhcpServiceInfo(
hostname="Pentair: 01-01-01",
ip="1.1.1.1",
macaddress="AA:BB:CC:DD:EE:FF",
),
)

View File

@ -321,7 +321,9 @@ async def test_discovered_by_discovery_and_dhcp(hass):
result3 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress="00:00:00:00:00:00"),
data=dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="00:00:00:00:00:00", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT
@ -331,7 +333,9 @@ async def test_discovered_by_discovery_and_dhcp(hass):
result3 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip="1.2.3.5", macaddress="00:00:00:00:00:01"),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.5", macaddress="00:00:00:00:00:01", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT

View File

@ -176,7 +176,9 @@ async def test_dhcp(hass: HomeAssistant) -> None:
"""Test that DHCP discovery works."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=dhcp.DhcpServiceInfo(macaddress="01:23:45:67:89:ab"),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.4", macaddress="01:23:45:67:89:ab", hostname="mock_hostname"
),
context={"source": config_entries.SOURCE_DHCP},
)

View File

@ -471,7 +471,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result2 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff"),
data=dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_ABORT
@ -483,7 +485,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result3 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress="00:00:00:00:00:00"),
data=dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="00:00:00:00:00:00", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT
@ -497,7 +501,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result3 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data=dhcp.DhcpServiceInfo(ip="1.2.3.5", macaddress="00:00:00:00:00:01"),
data=dhcp.DhcpServiceInfo(
ip="1.2.3.5", macaddress="00:00:00:00:00:01", hostname="mock_hostname"
),
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT
@ -509,7 +515,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
[
(
config_entries.SOURCE_DHCP,
dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff"),
dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff", hostname="mock_hostname"
),
),
(
config_entries.SOURCE_HOMEKIT,
@ -570,7 +578,9 @@ async def test_discovered_by_dhcp_or_homekit(hass, source, data):
[
(
config_entries.SOURCE_DHCP,
dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff"),
dhcp.DhcpServiceInfo(
ip=IP_ADDRESS, macaddress="aa:bb:cc:dd:ee:ff", hostname="mock_hostname"
),
),
(
config_entries.SOURCE_HOMEKIT,