Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions homeassistant/components/dhcp/__init__.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -32,12 +33,14 @@
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
Expand All @@ -55,13 +58,33 @@
_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."""
Expand Down
8 changes: 7 additions & 1 deletion homeassistant/data_entry_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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
Expand All @@ -25,6 +26,11 @@
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."""

Expand Down Expand Up @@ -301,7 +307,7 @@ async def _async_handle_step(
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."""
Expand Down
14 changes: 10 additions & 4 deletions homeassistant/helpers/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.

Expand All @@ -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,
Expand Down
12 changes: 9 additions & 3 deletions tests/components/gogogate2/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"] == {}
Expand Down Expand Up @@ -246,15 +248,19 @@ 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"

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"
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
)

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 = [
Expand Down
12 changes: 9 additions & 3 deletions tests/components/samsungtv/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@
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",
Expand Down Expand Up @@ -1096,7 +1098,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
Expand Down Expand Up @@ -1130,7 +1134,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
Expand Down
1 change: 1 addition & 0 deletions tests/components/screenlogic/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
),
)

Expand Down
8 changes: 6 additions & 2 deletions tests/components/tplink/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion tests/components/verisure/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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},
)

Expand Down
20 changes: 15 additions & 5 deletions tests/components/yeelight/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down