Refactor zeroconf matching to be more DRY (#60293)

pull/60124/head
J. Nick Koston 2021-11-24 12:57:13 -06:00 committed by GitHub
parent 86cd46a0dd
commit fd116fc408
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 37 additions and 43 deletions

View File

@ -48,6 +48,18 @@ HOMEKIT_TYPES = [
"_hap._udp.local.",
]
# Keys we support matching against in properties that are always matched in
# upper case. ex: ZeroconfServiceInfo.properties["macaddress"]
UPPER_MATCH_PROPS = {"macaddress"}
# Keys we support matching against in properties that are always matched in
# lower case. ex: ZeroconfServiceInfo.properties["model"]
LOWER_MATCH_PROPS = {"manufacturer", "model"}
# Top level keys we support matching against in properties that are always matched in
# lower case. ex: ZeroconfServiceInfo.name
LOWER_MATCH_ATTRS = {"name"}
# Everything we support matching
ALL_MATCHERS = UPPER_MATCH_PROPS | LOWER_MATCH_PROPS | LOWER_MATCH_ATTRS
CONF_DEFAULT_INTERFACE = "default_interface"
CONF_IPV6 = "ipv6"
DEFAULT_DEFAULT_INTERFACE = True
@ -306,6 +318,18 @@ async def _async_register_hass_zc_service(
await aio_zc.async_register_service(info, allow_name_change=True)
def _match_against_data(matcher: dict[str, str], match_data: dict[str, str]) -> bool:
"""Check a matcher to ensure all values in match_data match."""
return not any(
key
for key in ALL_MATCHERS
if key in matcher
and (
key not in match_data or not fnmatch.fnmatch(match_data[key], matcher[key])
)
)
class ZeroconfDiscovery:
"""Discovery via zeroconf."""
@ -380,10 +404,10 @@ class ZeroconfDiscovery:
return
_LOGGER.debug("Discovered new device %s %s", name, info)
props: dict[str, str] = info.properties
# If we can handle it as a HomeKit discovery, we do that here.
if service_type in HOMEKIT_TYPES:
props = info.properties
if domain := async_get_homekit_discovery_domain(self.homekit_models, props):
discovery_flow.async_create_flow(
self.hass, domain, {"source": config_entries.SOURCE_HOMEKIT}, info
@ -405,52 +429,22 @@ class ZeroconfDiscovery:
# likely bad homekit data
return
if info.name:
lowercase_name: str | None = info.name.lower()
else:
lowercase_name = None
if "macaddress" in info.properties:
uppercase_mac: str | None = info.properties["macaddress"].upper()
else:
uppercase_mac = None
if "manufacturer" in info.properties:
lowercase_manufacturer: str | None = info.properties["manufacturer"].lower()
else:
lowercase_manufacturer = None
if "model" in info.properties:
lowercase_model: str | None = info.properties["model"].lower()
else:
lowercase_model = None
match_data: dict[str, str] = {}
for key in LOWER_MATCH_ATTRS:
attr_value: str = getattr(info, key)
match_data[key] = attr_value.lower()
for key in UPPER_MATCH_PROPS:
if key in props:
match_data[key] = props[key].upper()
for key in LOWER_MATCH_PROPS:
if key in props:
match_data[key] = props[key].lower()
# Not all homekit types are currently used for discovery
# so not all service type exist in zeroconf_types
for matcher in self.zeroconf_types.get(service_type, []):
if len(matcher) > 1:
if "macaddress" in matcher and (
uppercase_mac is None
or not fnmatch.fnmatch(uppercase_mac, matcher["macaddress"])
):
continue
if "name" in matcher and (
lowercase_name is None
or not fnmatch.fnmatch(lowercase_name, matcher["name"])
):
continue
if "manufacturer" in matcher and (
lowercase_manufacturer is None
or not fnmatch.fnmatch(
lowercase_manufacturer, matcher["manufacturer"]
)
):
continue
if "model" in matcher and (
lowercase_model is None
or not fnmatch.fnmatch(lowercase_model, matcher["model"])
):
continue
if len(matcher) > 1 and not _match_against_data(matcher, match_data):
continue
discovery_flow.async_create_flow(
self.hass,