From f069a37f7da0864f760710269eb3567aa33d5d56 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Feb 2022 11:02:52 -0600 Subject: [PATCH] Allow integrations to request dhcp discovery flows for registered devices (#66528) --- homeassistant/components/dhcp/__init__.py | 31 +- homeassistant/generated/dhcp.py | 780 ++++------------------ homeassistant/loader.py | 8 +- script/hassfest/dhcp.py | 15 +- script/hassfest/manifest.py | 1 + tests/components/dhcp/test_init.py | 49 +- tests/test_loader.py | 2 + 7 files changed, 239 insertions(+), 647 deletions(-) diff --git a/homeassistant/components/dhcp/__init__.py b/homeassistant/components/dhcp/__init__.py index dd247c4cab9..ff67f77257b 100644 --- a/homeassistant/components/dhcp/__init__.py +++ b/homeassistant/components/dhcp/__init__.py @@ -1,4 +1,5 @@ """The dhcp integration.""" +from __future__ import annotations from dataclasses import dataclass from datetime import timedelta @@ -35,7 +36,12 @@ from homeassistant.const import ( 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.device_registry import ( + CONNECTION_NETWORK_MAC, + DeviceRegistry, + async_get, + format_mac, +) from homeassistant.helpers.event import ( async_track_state_added_domain, async_track_time_interval, @@ -54,9 +60,11 @@ MESSAGE_TYPE = "message-type" HOSTNAME: Final = "hostname" MAC_ADDRESS: Final = "macaddress" IP_ADDRESS: Final = "ip" +REGISTERED_DEVICES: Final = "registered_devices" DHCP_REQUEST = 3 SCAN_INTERVAL = timedelta(minutes=60) + _LOGGER = logging.getLogger(__name__) @@ -180,7 +188,20 @@ class WatcherBase: ) matched_domains = set() + device_domains = set() + + dev_reg: DeviceRegistry = async_get(self.hass) + if device := dev_reg.async_get_device( + identifiers=set(), connections={(CONNECTION_NETWORK_MAC, uppercase_mac)} + ): + for entry_id in device.config_entries: + if entry := self.hass.config_entries.async_get_entry(entry_id): + device_domains.add(entry.domain) + for entry in self._integration_matchers: + if entry.get(REGISTERED_DEVICES) and not entry["domain"] in device_domains: + continue + if MAC_ADDRESS in entry and not fnmatch.fnmatch( uppercase_mac, entry[MAC_ADDRESS] ): @@ -192,14 +213,12 @@ class WatcherBase: continue _LOGGER.debug("Matched %s against %s", data, entry) - if entry["domain"] in matched_domains: - # Only match once per domain - continue - matched_domains.add(entry["domain"]) + + for domain in matched_domains: discovery_flow.async_create_flow( self.hass, - entry["domain"], + domain, {"source": config_entries.SOURCE_DHCP}, DhcpServiceInfo( ip=ip_address, diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 2fbefe9bbca..a10a2334c73 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -2,639 +2,153 @@ To update, run python3 -m script.hassfest """ +from __future__ import annotations # fmt: off -DHCP = [ - { - "domain": "august", - "hostname": "connect", - "macaddress": "D86162*" - }, - { - "domain": "august", - "hostname": "connect", - "macaddress": "B8B7F1*" - }, - { - "domain": "august", - "hostname": "connect", - "macaddress": "2C9FFB*" - }, - { - "domain": "august", - "hostname": "august*", - "macaddress": "E076D0*" - }, - { - "domain": "axis", - "hostname": "axis-00408c*", - "macaddress": "00408C*" - }, - { - "domain": "axis", - "hostname": "axis-accc8e*", - "macaddress": "ACCC8E*" - }, - { - "domain": "axis", - "hostname": "axis-b8a44f*", - "macaddress": "B8A44F*" - }, - { - "domain": "blink", - "hostname": "blink*", - "macaddress": "B85F98*" - }, - { - "domain": "blink", - "hostname": "blink*", - "macaddress": "00037F*" - }, - { - "domain": "blink", - "hostname": "blink*", - "macaddress": "20A171*" - }, - { - "domain": "broadlink", - "macaddress": "34EA34*" - }, - { - "domain": "broadlink", - "macaddress": "24DFA7*" - }, - { - "domain": "broadlink", - "macaddress": "A043B0*" - }, - { - "domain": "broadlink", - "macaddress": "B4430D*" - }, - { - "domain": "elkm1", - "macaddress": "00409D*" - }, - { - "domain": "emonitor", - "hostname": "emonitor*", - "macaddress": "0090C2*" - }, - { - "domain": "flume", - "hostname": "flume-gw-*" - }, - { - "domain": "flux_led", - "macaddress": "18B905*", - "hostname": "[ba][lk]*" - }, - { - "domain": "flux_led", - "macaddress": "249494*", - "hostname": "[ba][lk]*" - }, - { - "domain": "flux_led", - "macaddress": "7CB94C*", - "hostname": "[ba][lk]*" - }, - { - "domain": "flux_led", - "macaddress": "ACCF23*", - "hostname": "[hba][flk]*" - }, - { - "domain": "flux_led", - "macaddress": "B4E842*", - "hostname": "[ba][lk]*" - }, - { - "domain": "flux_led", - "macaddress": "F0FE6B*", - "hostname": "[hba][flk]*" - }, - { - "domain": "flux_led", - "macaddress": "8CCE4E*", - "hostname": "lwip*" - }, - { - "domain": "flux_led", - "hostname": "zengge_[0-9a-f][0-9a-f]_*" - }, - { - "domain": "flux_led", - "macaddress": "C82E47*", - "hostname": "sta*" - }, - { - "domain": "fronius", - "macaddress": "0003AC*" - }, - { - "domain": "goalzero", - "hostname": "yeti*" - }, - { - "domain": "gogogate2", - "hostname": "ismartgate*" - }, - { - "domain": "guardian", - "hostname": "gvc*", - "macaddress": "30AEA4*" - }, - { - "domain": "guardian", - "hostname": "gvc*", - "macaddress": "B4E62D*" - }, - { - "domain": "guardian", - "hostname": "guardian*", - "macaddress": "30AEA4*" - }, - { - "domain": "hunterdouglas_powerview", - "hostname": "hunter*", - "macaddress": "002674*" - }, - { - "domain": "isy994", - "hostname": "isy*", - "macaddress": "0021B9*" - }, - { - "domain": "lyric", - "hostname": "lyric-*", - "macaddress": "48A2E6*" - }, - { - "domain": "lyric", - "hostname": "lyric-*", - "macaddress": "B82CA0*" - }, - { - "domain": "lyric", - "hostname": "lyric-*", - "macaddress": "00D02D*" - }, - { - "domain": "myq", - "macaddress": "645299*" - }, - { - "domain": "nest", - "macaddress": "18B430*" - }, - { - "domain": "nest", - "macaddress": "641666*" - }, - { - "domain": "nest", - "macaddress": "D8EB46*" - }, - { - "domain": "nest", - "macaddress": "1C53F9*" - }, - { - "domain": "nexia", - "hostname": "xl857-*", - "macaddress": "000231*" - }, - { - "domain": "nuheat", - "hostname": "nuheat", - "macaddress": "002338*" - }, - { - "domain": "nuki", - "hostname": "nuki_bridge_*" - }, - { - "domain": "oncue", - "hostname": "kohlergen*", - "macaddress": "00146F*" - }, - { - "domain": "overkiz", - "hostname": "gateway*", - "macaddress": "F8811A*" - }, - { - "domain": "powerwall", - "hostname": "1118431-*" - }, - { - "domain": "rachio", - "hostname": "rachio-*", - "macaddress": "009D6B*" - }, - { - "domain": "rachio", - "hostname": "rachio-*", - "macaddress": "F0038C*" - }, - { - "domain": "rachio", - "hostname": "rachio-*", - "macaddress": "74C63B*" - }, - { - "domain": "rainforest_eagle", - "macaddress": "D8D5B9*" - }, - { - "domain": "ring", - "hostname": "ring*", - "macaddress": "0CAE7D*" - }, - { - "domain": "roomba", - "hostname": "irobot-*", - "macaddress": "501479*" - }, - { - "domain": "roomba", - "hostname": "roomba-*", - "macaddress": "80A589*" - }, - { - "domain": "roomba", - "hostname": "roomba-*", - "macaddress": "DCF505*" - }, - { - "domain": "samsungtv", - "hostname": "tizen*" - }, - { - "domain": "samsungtv", - "macaddress": "8CC8CD*" - }, - { - "domain": "samsungtv", - "macaddress": "606BBD*" - }, - { - "domain": "samsungtv", - "macaddress": "F47B5E*" - }, - { - "domain": "samsungtv", - "macaddress": "4844F7*" - }, - { - "domain": "screenlogic", - "hostname": "pentair: *", - "macaddress": "00C033*" - }, - { - "domain": "sense", - "hostname": "sense-*", - "macaddress": "009D6B*" - }, - { - "domain": "sense", - "hostname": "sense-*", - "macaddress": "DCEFCA*" - }, - { - "domain": "sense", - "hostname": "sense-*", - "macaddress": "A4D578*" - }, - { - "domain": "senseme", - "macaddress": "20F85E*" - }, - { - "domain": "sensibo", - "hostname": "sensibo*" - }, - { - "domain": "simplisafe", - "hostname": "simplisafe*", - "macaddress": "30AEA4*" - }, - { - "domain": "smartthings", - "hostname": "st*", - "macaddress": "24FD5B*" - }, - { - "domain": "smartthings", - "hostname": "smartthings*", - "macaddress": "24FD5B*" - }, - { - "domain": "smartthings", - "hostname": "hub*", - "macaddress": "24FD5B*" - }, - { - "domain": "smartthings", - "hostname": "hub*", - "macaddress": "D052A8*" - }, - { - "domain": "smartthings", - "hostname": "hub*", - "macaddress": "286D97*" - }, - { - "domain": "solaredge", - "hostname": "target", - "macaddress": "002702*" - }, - { - "domain": "somfy_mylink", - "hostname": "somfy_*", - "macaddress": "B8B7F1*" - }, - { - "domain": "squeezebox", - "hostname": "squeezebox*", - "macaddress": "000420*" - }, - { - "domain": "steamist", - "macaddress": "001E0C*", - "hostname": "my[45]50*" - }, - { - "domain": "tado", - "hostname": "tado*" - }, - { - "domain": "tesla_wall_connector", - "hostname": "teslawallconnector_*", - "macaddress": "DC44271*" - }, - { - "domain": "tesla_wall_connector", - "hostname": "teslawallconnector_*", - "macaddress": "98ED5C*" - }, - { - "domain": "tesla_wall_connector", - "hostname": "teslawallconnector_*", - "macaddress": "4CFCAA*" - }, - { - "domain": "tolo", - "hostname": "usr-tcp232-ed2" - }, - { - "domain": "toon", - "hostname": "eneco-*", - "macaddress": "74C63B*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "60A4B7*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "005F67*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "1027F5*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "403F8C*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "C0C9E3*" - }, - { - "domain": "tplink", - "hostname": "ep*", - "macaddress": "E848B8*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "E848B8*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "909A4A*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "1C3BF3*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "50C7BF*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "68FF7B*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "98DAC4*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "B09575*" - }, - { - "domain": "tplink", - "hostname": "hs*", - "macaddress": "C006C3*" - }, - { - "domain": "tplink", - "hostname": "ep*", - "macaddress": "003192*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "003192*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "1C3BF3*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "50C7BF*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "68FF7B*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "98DAC4*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "B09575*" - }, - { - "domain": "tplink", - "hostname": "k[lp]*", - "macaddress": "C006C3*" - }, - { - "domain": "tplink", - "hostname": "lb*", - "macaddress": "1C3BF3*" - }, - { - "domain": "tplink", - "hostname": "lb*", - "macaddress": "50C7BF*" - }, - { - "domain": "tplink", - "hostname": "lb*", - "macaddress": "68FF7B*" - }, - { - "domain": "tplink", - "hostname": "lb*", - "macaddress": "98DAC4*" - }, - { - "domain": "tplink", - "hostname": "lb*", - "macaddress": "B09575*" - }, - { - "domain": "tuya", - "macaddress": "105A17*" - }, - { - "domain": "tuya", - "macaddress": "10D561*" - }, - { - "domain": "tuya", - "macaddress": "1869D8*" - }, - { - "domain": "tuya", - "macaddress": "381F8D*" - }, - { - "domain": "tuya", - "macaddress": "508A06*" - }, - { - "domain": "tuya", - "macaddress": "68572D*" - }, - { - "domain": "tuya", - "macaddress": "708976*" - }, - { - "domain": "tuya", - "macaddress": "7CF666*" - }, - { - "domain": "tuya", - "macaddress": "84E342*" - }, - { - "domain": "tuya", - "macaddress": "D4A651*" - }, - { - "domain": "tuya", - "macaddress": "D81F12*" - }, - { - "domain": "twinkly", - "hostname": "twinkly_*" - }, - { - "domain": "unifiprotect", - "macaddress": "B4FBE4*" - }, - { - "domain": "unifiprotect", - "macaddress": "802AA8*" - }, - { - "domain": "unifiprotect", - "macaddress": "F09FC2*" - }, - { - "domain": "unifiprotect", - "macaddress": "68D79A*" - }, - { - "domain": "unifiprotect", - "macaddress": "18E829*" - }, - { - "domain": "unifiprotect", - "macaddress": "245A4C*" - }, - { - "domain": "unifiprotect", - "macaddress": "784558*" - }, - { - "domain": "unifiprotect", - "macaddress": "E063DA*" - }, - { - "domain": "unifiprotect", - "macaddress": "265A4C*" - }, - { - "domain": "unifiprotect", - "macaddress": "74ACB9*" - }, - { - "domain": "verisure", - "macaddress": "0023C1*" - }, - { - "domain": "vicare", - "macaddress": "B87424*" - }, - { - "domain": "wiz", - "macaddress": "A8BB50*" - }, - { - "domain": "wiz", - "hostname": "wiz_*" - }, - { - "domain": "yeelight", - "hostname": "yeelink-*" - } -] +DHCP: list[dict[str, str | bool]] = [ + {'domain': 'august', 'hostname': 'connect', 'macaddress': 'D86162*'}, + {'domain': 'august', 'hostname': 'connect', 'macaddress': 'B8B7F1*'}, + {'domain': 'august', 'hostname': 'connect', 'macaddress': '2C9FFB*'}, + {'domain': 'august', 'hostname': 'august*', 'macaddress': 'E076D0*'}, + {'domain': 'axis', 'hostname': 'axis-00408c*', 'macaddress': '00408C*'}, + {'domain': 'axis', 'hostname': 'axis-accc8e*', 'macaddress': 'ACCC8E*'}, + {'domain': 'axis', 'hostname': 'axis-b8a44f*', 'macaddress': 'B8A44F*'}, + {'domain': 'blink', 'hostname': 'blink*', 'macaddress': 'B85F98*'}, + {'domain': 'blink', 'hostname': 'blink*', 'macaddress': '00037F*'}, + {'domain': 'blink', 'hostname': 'blink*', 'macaddress': '20A171*'}, + {'domain': 'broadlink', 'macaddress': '34EA34*'}, + {'domain': 'broadlink', 'macaddress': '24DFA7*'}, + {'domain': 'broadlink', 'macaddress': 'A043B0*'}, + {'domain': 'broadlink', 'macaddress': 'B4430D*'}, + {'domain': 'elkm1', 'macaddress': '00409D*'}, + {'domain': 'emonitor', 'hostname': 'emonitor*', 'macaddress': '0090C2*'}, + {'domain': 'flume', 'hostname': 'flume-gw-*'}, + {'domain': 'flux_led', 'hostname': '[ba][lk]*', 'macaddress': '18B905*'}, + {'domain': 'flux_led', 'hostname': '[ba][lk]*', 'macaddress': '249494*'}, + {'domain': 'flux_led', 'hostname': '[ba][lk]*', 'macaddress': '7CB94C*'}, + {'domain': 'flux_led', 'hostname': '[hba][flk]*', 'macaddress': 'ACCF23*'}, + {'domain': 'flux_led', 'hostname': '[ba][lk]*', 'macaddress': 'B4E842*'}, + {'domain': 'flux_led', 'hostname': '[hba][flk]*', 'macaddress': 'F0FE6B*'}, + {'domain': 'flux_led', 'hostname': 'lwip*', 'macaddress': '8CCE4E*'}, + {'domain': 'flux_led', 'hostname': 'zengge_[0-9a-f][0-9a-f]_*'}, + {'domain': 'flux_led', 'hostname': 'sta*', 'macaddress': 'C82E47*'}, + {'domain': 'fronius', 'macaddress': '0003AC*'}, + {'domain': 'goalzero', 'hostname': 'yeti*'}, + {'domain': 'gogogate2', 'hostname': 'ismartgate*'}, + {'domain': 'guardian', 'hostname': 'gvc*', 'macaddress': '30AEA4*'}, + {'domain': 'guardian', 'hostname': 'gvc*', 'macaddress': 'B4E62D*'}, + {'domain': 'guardian', 'hostname': 'guardian*', 'macaddress': '30AEA4*'}, + {'domain': 'hunterdouglas_powerview', + 'hostname': 'hunter*', + 'macaddress': '002674*'}, + {'domain': 'isy994', 'hostname': 'isy*', 'macaddress': '0021B9*'}, + {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '48A2E6*'}, + {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': 'B82CA0*'}, + {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '00D02D*'}, + {'domain': 'myq', 'macaddress': '645299*'}, + {'domain': 'nest', 'macaddress': '18B430*'}, + {'domain': 'nest', 'macaddress': '641666*'}, + {'domain': 'nest', 'macaddress': 'D8EB46*'}, + {'domain': 'nest', 'macaddress': '1C53F9*'}, + {'domain': 'nexia', 'hostname': 'xl857-*', 'macaddress': '000231*'}, + {'domain': 'nuheat', 'hostname': 'nuheat', 'macaddress': '002338*'}, + {'domain': 'nuki', 'hostname': 'nuki_bridge_*'}, + {'domain': 'oncue', 'hostname': 'kohlergen*', 'macaddress': '00146F*'}, + {'domain': 'overkiz', 'hostname': 'gateway*', 'macaddress': 'F8811A*'}, + {'domain': 'powerwall', 'hostname': '1118431-*'}, + {'domain': 'rachio', 'hostname': 'rachio-*', 'macaddress': '009D6B*'}, + {'domain': 'rachio', 'hostname': 'rachio-*', 'macaddress': 'F0038C*'}, + {'domain': 'rachio', 'hostname': 'rachio-*', 'macaddress': '74C63B*'}, + {'domain': 'rainforest_eagle', 'macaddress': 'D8D5B9*'}, + {'domain': 'ring', 'hostname': 'ring*', 'macaddress': '0CAE7D*'}, + {'domain': 'roomba', 'hostname': 'irobot-*', 'macaddress': '501479*'}, + {'domain': 'roomba', 'hostname': 'roomba-*', 'macaddress': '80A589*'}, + {'domain': 'roomba', 'hostname': 'roomba-*', 'macaddress': 'DCF505*'}, + {'domain': 'samsungtv', 'hostname': 'tizen*'}, + {'domain': 'samsungtv', 'macaddress': '8CC8CD*'}, + {'domain': 'samsungtv', 'macaddress': '606BBD*'}, + {'domain': 'samsungtv', 'macaddress': 'F47B5E*'}, + {'domain': 'samsungtv', 'macaddress': '4844F7*'}, + {'domain': 'screenlogic', 'hostname': 'pentair: *', 'macaddress': '00C033*'}, + {'domain': 'sense', 'hostname': 'sense-*', 'macaddress': '009D6B*'}, + {'domain': 'sense', 'hostname': 'sense-*', 'macaddress': 'DCEFCA*'}, + {'domain': 'sense', 'hostname': 'sense-*', 'macaddress': 'A4D578*'}, + {'domain': 'senseme', 'macaddress': '20F85E*'}, + {'domain': 'sensibo', 'hostname': 'sensibo*'}, + {'domain': 'simplisafe', 'hostname': 'simplisafe*', 'macaddress': '30AEA4*'}, + {'domain': 'smartthings', 'hostname': 'st*', 'macaddress': '24FD5B*'}, + {'domain': 'smartthings', 'hostname': 'smartthings*', 'macaddress': '24FD5B*'}, + {'domain': 'smartthings', 'hostname': 'hub*', 'macaddress': '24FD5B*'}, + {'domain': 'smartthings', 'hostname': 'hub*', 'macaddress': 'D052A8*'}, + {'domain': 'smartthings', 'hostname': 'hub*', 'macaddress': '286D97*'}, + {'domain': 'solaredge', 'hostname': 'target', 'macaddress': '002702*'}, + {'domain': 'somfy_mylink', 'hostname': 'somfy_*', 'macaddress': 'B8B7F1*'}, + {'domain': 'squeezebox', 'hostname': 'squeezebox*', 'macaddress': '000420*'}, + {'domain': 'steamist', 'hostname': 'my[45]50*', 'macaddress': '001E0C*'}, + {'domain': 'tado', 'hostname': 'tado*'}, + {'domain': 'tesla_wall_connector', + 'hostname': 'teslawallconnector_*', + 'macaddress': 'DC44271*'}, + {'domain': 'tesla_wall_connector', + 'hostname': 'teslawallconnector_*', + 'macaddress': '98ED5C*'}, + {'domain': 'tesla_wall_connector', + 'hostname': 'teslawallconnector_*', + 'macaddress': '4CFCAA*'}, + {'domain': 'tolo', 'hostname': 'usr-tcp232-ed2'}, + {'domain': 'toon', 'hostname': 'eneco-*', 'macaddress': '74C63B*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '60A4B7*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '005F67*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '1027F5*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '403F8C*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'C0C9E3*'}, + {'domain': 'tplink', 'hostname': 'ep*', 'macaddress': 'E848B8*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'E848B8*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '909A4A*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': '1C3BF3*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': '50C7BF*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': '68FF7B*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': '98DAC4*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': 'B09575*'}, + {'domain': 'tplink', 'hostname': 'hs*', 'macaddress': 'C006C3*'}, + {'domain': 'tplink', 'hostname': 'ep*', 'macaddress': '003192*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '003192*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '1C3BF3*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '50C7BF*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '68FF7B*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '98DAC4*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'B09575*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'C006C3*'}, + {'domain': 'tplink', 'hostname': 'lb*', 'macaddress': '1C3BF3*'}, + {'domain': 'tplink', 'hostname': 'lb*', 'macaddress': '50C7BF*'}, + {'domain': 'tplink', 'hostname': 'lb*', 'macaddress': '68FF7B*'}, + {'domain': 'tplink', 'hostname': 'lb*', 'macaddress': '98DAC4*'}, + {'domain': 'tplink', 'hostname': 'lb*', 'macaddress': 'B09575*'}, + {'domain': 'tuya', 'macaddress': '105A17*'}, + {'domain': 'tuya', 'macaddress': '10D561*'}, + {'domain': 'tuya', 'macaddress': '1869D8*'}, + {'domain': 'tuya', 'macaddress': '381F8D*'}, + {'domain': 'tuya', 'macaddress': '508A06*'}, + {'domain': 'tuya', 'macaddress': '68572D*'}, + {'domain': 'tuya', 'macaddress': '708976*'}, + {'domain': 'tuya', 'macaddress': '7CF666*'}, + {'domain': 'tuya', 'macaddress': '84E342*'}, + {'domain': 'tuya', 'macaddress': 'D4A651*'}, + {'domain': 'tuya', 'macaddress': 'D81F12*'}, + {'domain': 'twinkly', 'hostname': 'twinkly_*'}, + {'domain': 'unifiprotect', 'macaddress': 'B4FBE4*'}, + {'domain': 'unifiprotect', 'macaddress': '802AA8*'}, + {'domain': 'unifiprotect', 'macaddress': 'F09FC2*'}, + {'domain': 'unifiprotect', 'macaddress': '68D79A*'}, + {'domain': 'unifiprotect', 'macaddress': '18E829*'}, + {'domain': 'unifiprotect', 'macaddress': '245A4C*'}, + {'domain': 'unifiprotect', 'macaddress': '784558*'}, + {'domain': 'unifiprotect', 'macaddress': 'E063DA*'}, + {'domain': 'unifiprotect', 'macaddress': '265A4C*'}, + {'domain': 'unifiprotect', 'macaddress': '74ACB9*'}, + {'domain': 'verisure', 'macaddress': '0023C1*'}, + {'domain': 'vicare', 'macaddress': 'B87424*'}, + {'domain': 'wiz', 'macaddress': 'A8BB50*'}, + {'domain': 'wiz', 'hostname': 'wiz_*'}, + {'domain': 'yeelight', 'hostname': 'yeelink-*'}] diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 7217fd5940b..c02fa18eefb 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -82,7 +82,7 @@ class Manifest(TypedDict, total=False): mqtt: list[str] ssdp: list[dict[str, str]] zeroconf: list[str | dict[str, str]] - dhcp: list[dict[str, str]] + dhcp: list[dict[str, bool | str]] usb: list[dict[str, str]] homekit: dict[str, list[str]] is_built_in: bool @@ -228,9 +228,9 @@ async def async_get_zeroconf( return zeroconf -async def async_get_dhcp(hass: HomeAssistant) -> list[dict[str, str]]: +async def async_get_dhcp(hass: HomeAssistant) -> list[dict[str, str | bool]]: """Return cached list of dhcp types.""" - dhcp: list[dict[str, str]] = DHCP.copy() + dhcp: list[dict[str, str | bool]] = DHCP.copy() integrations = await async_get_custom_components(hass) for integration in integrations.values(): @@ -474,7 +474,7 @@ class Integration: return self.manifest.get("zeroconf") @property - def dhcp(self) -> list[dict[str, str]] | None: + def dhcp(self) -> list[dict[str, str | bool]] | None: """Return Integration dhcp entries.""" return self.manifest.get("dhcp") diff --git a/script/hassfest/dhcp.py b/script/hassfest/dhcp.py index c746c64e46f..1aca6a1f68d 100644 --- a/script/hassfest/dhcp.py +++ b/script/hassfest/dhcp.py @@ -1,7 +1,8 @@ """Generate dhcp file.""" from __future__ import annotations -import json +import pprint +import re from .model import Config, Integration @@ -10,10 +11,11 @@ BASE = """ To update, run python3 -m script.hassfest \"\"\" +from __future__ import annotations # fmt: off -DHCP = {} +DHCP: list[dict[str, str | bool]] = {} """.strip() @@ -35,7 +37,14 @@ def generate_and_validate(integrations: list[dict[str, str]]): for entry in match_types: match_list.append({"domain": domain, **entry}) - return BASE.format(json.dumps(match_list, indent=4)) + # JSON will format `True` as `true` + # re.sub for flake8 E128 + formatted = pprint.pformat(match_list) + formatted_aligned_continuation = re.sub(r"^\[\{", "[\n {", formatted) + formatted_align_indent = re.sub( + r"(?m)^ ", " ", formatted_aligned_continuation, flags=re.MULTILINE, count=0 + ) + return BASE.format(formatted_align_indent) def validate(integrations: dict[str, Integration], config: Config): diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 55cfd44bae9..d146621b416 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -218,6 +218,7 @@ MANIFEST_SCHEMA = vol.Schema( str, verify_uppercase, verify_wildcard ), vol.Optional("hostname"): vol.All(str, verify_lowercase), + vol.Optional("registered_devices"): cv.boolean, } ) ], diff --git a/tests/components/dhcp/test_init.py b/tests/components/dhcp/test_init.py index 0956230d787..3650ed32987 100644 --- a/tests/components/dhcp/test_init.py +++ b/tests/components/dhcp/test_init.py @@ -25,10 +25,11 @@ from homeassistant.const import ( STATE_HOME, STATE_NOT_HOME, ) +import homeassistant.helpers.device_registry as dr from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import async_fire_time_changed +from tests.common import MockConfigEntry, async_fire_time_changed # connect b8:b7:f1:6d:b5:33 192.168.210.56 RAW_DHCP_REQUEST = ( @@ -207,6 +208,52 @@ async def test_dhcp_renewal_match_hostname_and_macaddress(hass): ) +async def test_registered_devices(hass): + """Test discovery flows are created for registered devices.""" + integration_matchers = [ + {"domain": "not-matching", "registered_devices": True}, + {"domain": "mock-domain", "registered_devices": True}, + ] + + packet = Ether(RAW_DHCP_RENEWAL) + + registry = dr.async_get(hass) + config_entry = MockConfigEntry(domain="mock-domain", data={}) + config_entry.add_to_hass(hass) + registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, "50147903852c")}, + name="name", + ) + # Not enabled should not get flows + config_entry2 = MockConfigEntry(domain="mock-domain-2", data={}) + config_entry2.add_to_hass(hass) + registry.async_get_or_create( + config_entry_id=config_entry2.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, "50147903852c")}, + name="name", + ) + + async_handle_dhcp_packet = await _async_get_handle_dhcp_packet( + hass, integration_matchers + ) + with patch.object(hass.config_entries.flow, "async_init") as mock_init: + await async_handle_dhcp_packet(packet) + # Ensure no change is ignored + await async_handle_dhcp_packet(packet) + + assert len(mock_init.mock_calls) == 1 + assert mock_init.mock_calls[0][1][0] == "mock-domain" + assert mock_init.mock_calls[0][2]["context"] == { + "source": config_entries.SOURCE_DHCP + } + assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo( + ip="192.168.1.120", + hostname="irobot-ae9ec12dd3b04885bcbfa36afb01e1cc", + macaddress="50147903852c", + ) + + async def test_dhcp_match_hostname(hass): """Test matching based on hostname only.""" integration_matchers = [{"domain": "mock-domain", "hostname": "connect"}] diff --git a/tests/test_loader.py b/tests/test_loader.py index 8cc923840c2..9f2aaff58b7 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -203,6 +203,7 @@ def test_integration_properties(hass): {"hostname": "tesla_*", "macaddress": "4CFCAA*"}, {"hostname": "tesla_*", "macaddress": "044EAF*"}, {"hostname": "tesla_*", "macaddress": "98ED5C*"}, + {"registered_devices": True}, ], "usb": [ {"vid": "10C4", "pid": "EA60"}, @@ -233,6 +234,7 @@ def test_integration_properties(hass): {"hostname": "tesla_*", "macaddress": "4CFCAA*"}, {"hostname": "tesla_*", "macaddress": "044EAF*"}, {"hostname": "tesla_*", "macaddress": "98ED5C*"}, + {"registered_devices": True}, ] assert integration.usb == [ {"vid": "10C4", "pid": "EA60"},