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.""" """The dhcp integration."""
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import fnmatch import fnmatch
from ipaddress import ip_address as make_ip_address from ipaddress import ip_address as make_ip_address
import logging import logging
import os import os
import threading import threading
from typing import Final, TypedDict from typing import Any, Final
from aiodiscover import DiscoverHosts from aiodiscover import DiscoverHosts
from aiodiscover.discovery import ( from aiodiscover.discovery import (
@ -32,12 +33,14 @@ from homeassistant.const import (
STATE_HOME, STATE_HOME,
) )
from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.core import Event, HomeAssistant, State, callback
from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.helpers import discovery_flow from homeassistant.helpers import discovery_flow
from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_added_domain, async_track_state_added_domain,
async_track_time_interval, async_track_time_interval,
) )
from homeassistant.helpers.frame import report
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import async_get_dhcp from homeassistant.loader import async_get_dhcp
from homeassistant.util.async_ import run_callback_threadsafe from homeassistant.util.async_ import run_callback_threadsafe
@ -55,13 +58,33 @@ SCAN_INTERVAL = timedelta(minutes=60)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class DhcpServiceInfo(TypedDict): @dataclass
class DhcpServiceInfo(BaseServiceInfo):
"""Prepared info from dhcp entries.""" """Prepared info from dhcp entries."""
ip: str ip: str # pylint: disable=invalid-name
hostname: str hostname: str
macaddress: 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: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the dhcp component.""" """Set up the dhcp component."""

View File

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

View File

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

View File

@ -191,7 +191,9 @@ async def test_discovered_dhcp(
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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["type"] == RESULT_TYPE_FORM
assert result["errors"] == {} assert result["errors"] == {}
@ -246,7 +248,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
result2 = await hass.config_entries.flow.async_init( result2 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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["type"] == RESULT_TYPE_ABORT
assert result2["reason"] == "already_in_progress" 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( result3 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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["type"] == RESULT_TYPE_ABORT
assert result3["reason"] == "already_in_progress" assert result3["reason"] == "already_in_progress"

View File

@ -23,7 +23,9 @@ ZEROCONF_DISCOVERY_INFO = zeroconf.ZeroconfServiceInfo(
) )
DHCP_DISCOVERY_INFO = dhcp.DhcpServiceInfo( 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 = [ DISCOVERY_DATA = [

View File

@ -84,7 +84,9 @@ MOCK_SSDP_DATA_WRONGMODEL = {
ATTR_UPNP_MODEL_NAME: "HW-Qfake", ATTR_UPNP_MODEL_NAME: "HW-Qfake",
ATTR_UPNP_UDN: "uuid:0d1cef00-00dc-1000-9c80-4844f7b172df", 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" EXISTING_IP = "192.168.40.221"
MOCK_ZEROCONF_DATA = zeroconf.ZeroconfServiceInfo( MOCK_ZEROCONF_DATA = zeroconf.ZeroconfServiceInfo(
host="fake_host", 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( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1 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( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1 assert len(mock_setup.mock_calls) == 1

View File

@ -141,6 +141,7 @@ async def test_dhcp(hass):
data=dhcp.DhcpServiceInfo( data=dhcp.DhcpServiceInfo(
hostname="Pentair: 01-01-01", hostname="Pentair: 01-01-01",
ip="1.1.1.1", 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( result3 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT 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( result3 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT assert result3["type"] == RESULT_TYPE_ABORT

View File

@ -176,7 +176,9 @@ async def test_dhcp(hass: HomeAssistant) -> None:
"""Test that DHCP discovery works.""" """Test that DHCP discovery works."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, 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}, 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( result2 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_ABORT 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( result3 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT 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( result3 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_DHCP}, 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() await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_ABORT assert result3["type"] == RESULT_TYPE_ABORT
@ -509,7 +515,9 @@ async def test_discovered_by_homekit_and_dhcp(hass):
[ [
( (
config_entries.SOURCE_DHCP, 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, config_entries.SOURCE_HOMEKIT,
@ -570,7 +578,9 @@ async def test_discovered_by_dhcp_or_homekit(hass, source, data):
[ [
( (
config_entries.SOURCE_DHCP, 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, config_entries.SOURCE_HOMEKIT,