Refactor zeroconf task handling (#88204)
* Refactor zeroconf task handling
- Avoid the need to create tasks for most callbacks
- Fixes the untracked task that could get unexpectedly GCed
* be consistant
* be consistant
* fix zeroconf tests
* runtime
* Revert "runtime"
This reverts commit 19e6b61837
.
* precalc
* refactor
* tweak
* update tests
pull/88233/head
parent
c0e22be7a8
commit
c83ea297b5
|
@ -35,9 +35,8 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import (
|
||||
Integration,
|
||||
HomeKitDiscoveredIntegration,
|
||||
async_get_homekit,
|
||||
async_get_integration,
|
||||
async_get_zeroconf,
|
||||
bind_hass,
|
||||
)
|
||||
|
@ -348,7 +347,7 @@ class ZeroconfDiscovery:
|
|||
hass: HomeAssistant,
|
||||
zeroconf: HaZeroconf,
|
||||
zeroconf_types: dict[str, list[dict[str, str | dict[str, str]]]],
|
||||
homekit_models: dict[str, str],
|
||||
homekit_models: dict[str, HomeKitDiscoveredIntegration],
|
||||
ipv6: bool,
|
||||
) -> None:
|
||||
"""Init discovery."""
|
||||
|
@ -398,12 +397,6 @@ class ZeroconfDiscovery:
|
|||
if state_change == ServiceStateChange.Removed:
|
||||
return
|
||||
|
||||
asyncio.create_task(self._process_service_update(zeroconf, service_type, name))
|
||||
|
||||
async def _process_service_update(
|
||||
self, zeroconf: HaZeroconf, service_type: str, name: str
|
||||
) -> None:
|
||||
"""Process a zeroconf update."""
|
||||
try:
|
||||
async_service_info = AsyncServiceInfo(service_type, name)
|
||||
except BadTypeInNameException as ex:
|
||||
|
@ -411,24 +404,53 @@ class ZeroconfDiscovery:
|
|||
# This is a bug in the device firmware and we should ignore it
|
||||
_LOGGER.debug("Bad name in zeroconf record: %s: %s", name, ex)
|
||||
return
|
||||
await async_service_info.async_request(zeroconf, 3000)
|
||||
|
||||
if async_service_info.load_from_cache(zeroconf):
|
||||
self._async_process_service_update(async_service_info, service_type, name)
|
||||
else:
|
||||
self.hass.async_create_task(
|
||||
self._async_lookup_and_process_service_update(
|
||||
zeroconf, async_service_info, service_type, name
|
||||
)
|
||||
)
|
||||
|
||||
async def _async_lookup_and_process_service_update(
|
||||
self,
|
||||
zeroconf: HaZeroconf,
|
||||
async_service_info: AsyncServiceInfo,
|
||||
service_type: str,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Update and process a zeroconf update."""
|
||||
await async_service_info.async_request(zeroconf, 3000)
|
||||
self._async_process_service_update(async_service_info, service_type, name)
|
||||
|
||||
@callback
|
||||
def _async_process_service_update(
|
||||
self, async_service_info: AsyncServiceInfo, service_type: str, name: str
|
||||
) -> None:
|
||||
"""Process a zeroconf update."""
|
||||
info = info_from_service(async_service_info)
|
||||
if not info:
|
||||
# Prevent the browser thread from collapsing
|
||||
_LOGGER.debug("Failed to get addresses for device %s", name)
|
||||
return
|
||||
|
||||
_LOGGER.debug("Discovered new device %s %s", name, info)
|
||||
props: dict[str, str] = info.properties
|
||||
domain = None
|
||||
|
||||
# If we can handle it as a HomeKit discovery, we do that here.
|
||||
if service_type in HOMEKIT_TYPES and (
|
||||
domain := async_get_homekit_discovery_domain(self.homekit_models, props)
|
||||
homekit_model := async_get_homekit_discovery_domain(
|
||||
self.homekit_models, props
|
||||
)
|
||||
):
|
||||
domain = homekit_model.domain
|
||||
discovery_flow.async_create_flow(
|
||||
self.hass, domain, {"source": config_entries.SOURCE_HOMEKIT}, info
|
||||
self.hass,
|
||||
homekit_model.domain,
|
||||
{"source": config_entries.SOURCE_HOMEKIT},
|
||||
info,
|
||||
)
|
||||
# Continue on here as homekit_controller
|
||||
# still needs to get updates on devices
|
||||
|
@ -437,27 +459,15 @@ class ZeroconfDiscovery:
|
|||
# We only send updates to homekit_controller
|
||||
# if the device is already paired in order to avoid
|
||||
# offering a second discovery for the same device
|
||||
if not is_homekit_paired(props):
|
||||
integration: Integration = await async_get_integration(
|
||||
self.hass, domain
|
||||
)
|
||||
# Since we prefer local control, if the integration that is being
|
||||
# discovered is cloud AND the homekit device is UNPAIRED we still
|
||||
# want to discovery it.
|
||||
if not is_homekit_paired(props) and not homekit_model.always_discover:
|
||||
# If the device is paired with HomeKit we must send on
|
||||
# the update to homekit_controller so it can see when
|
||||
# the 'c#' field is updated. This is used to detect
|
||||
# when the device has been reset or updated.
|
||||
#
|
||||
# Additionally if the integration is polling, HKC offers a local
|
||||
# push experience for the user to control the device so we want
|
||||
# to offer that as well.
|
||||
#
|
||||
# As soon as the device becomes paired, the config flow will be
|
||||
# dismissed in the event the user does not want to pair
|
||||
# with Home Assistant.
|
||||
#
|
||||
if not integration.iot_class or (
|
||||
not integration.iot_class.startswith("cloud")
|
||||
and "polling" not in integration.iot_class
|
||||
):
|
||||
return
|
||||
# If the device is not paired and we should not always
|
||||
# discover it, we can stop here.
|
||||
return
|
||||
|
||||
match_data: dict[str, str] = {}
|
||||
for key in LOWER_MATCH_ATTRS:
|
||||
|
@ -495,8 +505,8 @@ class ZeroconfDiscovery:
|
|||
|
||||
|
||||
def async_get_homekit_discovery_domain(
|
||||
homekit_models: dict[str, str], props: dict[str, Any]
|
||||
) -> str | None:
|
||||
homekit_models: dict[str, HomeKitDiscoveredIntegration], props: dict[str, Any]
|
||||
) -> HomeKitDiscoveredIntegration | None:
|
||||
"""Handle a HomeKit discovery.
|
||||
|
||||
Return the domain to forward the discovery data to
|
||||
|
|
|
@ -4,65 +4,242 @@ To update, run python3 -m script.hassfest
|
|||
"""
|
||||
|
||||
HOMEKIT = {
|
||||
"3810X": "roku",
|
||||
"3820X": "roku",
|
||||
"4660X": "roku",
|
||||
"7820X": "roku",
|
||||
"819LMB": "myq",
|
||||
"AC02": "tado",
|
||||
"Abode": "abode",
|
||||
"BSB002": "hue",
|
||||
"C105X": "roku",
|
||||
"C135X": "roku",
|
||||
"EB-*": "ecobee",
|
||||
"Escea": "escea",
|
||||
"HHKBridge*": "hive",
|
||||
"Healty Home Coach": "netatmo",
|
||||
"Iota": "abode",
|
||||
"LIFX A19": "lifx",
|
||||
"LIFX BR30": "lifx",
|
||||
"LIFX Beam": "lifx",
|
||||
"LIFX Candle": "lifx",
|
||||
"LIFX Clean": "lifx",
|
||||
"LIFX Color": "lifx",
|
||||
"LIFX DLCOL": "lifx",
|
||||
"LIFX DLWW": "lifx",
|
||||
"LIFX Dlight": "lifx",
|
||||
"LIFX Downlight": "lifx",
|
||||
"LIFX Filament": "lifx",
|
||||
"LIFX GU10": "lifx",
|
||||
"LIFX Lightstrip": "lifx",
|
||||
"LIFX Mini": "lifx",
|
||||
"LIFX Nightvision": "lifx",
|
||||
"LIFX Pls": "lifx",
|
||||
"LIFX Plus": "lifx",
|
||||
"LIFX Tile": "lifx",
|
||||
"LIFX White": "lifx",
|
||||
"LIFX Z": "lifx",
|
||||
"MYQ": "myq",
|
||||
"NL29": "nanoleaf",
|
||||
"NL42": "nanoleaf",
|
||||
"NL47": "nanoleaf",
|
||||
"NL48": "nanoleaf",
|
||||
"NL52": "nanoleaf",
|
||||
"NL59": "nanoleaf",
|
||||
"Netatmo Relay": "netatmo",
|
||||
"PowerView": "hunterdouglas_powerview",
|
||||
"Presence": "netatmo",
|
||||
"Rachio": "rachio",
|
||||
"SPK5": "rainmachine",
|
||||
"Sensibo": "sensibo",
|
||||
"Smart Bridge": "lutron_caseta",
|
||||
"Socket": "wemo",
|
||||
"TRADFRI": "tradfri",
|
||||
"Touch HD": "rainmachine",
|
||||
"Welcome": "netatmo",
|
||||
"Wemo": "wemo",
|
||||
"YL*": "yeelight",
|
||||
"ecobee*": "ecobee",
|
||||
"iSmartGate": "gogogate2",
|
||||
"iZone": "izone",
|
||||
"tado": "tado",
|
||||
"3810X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"3820X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"4660X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"7820X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"819LMB": {
|
||||
"always_discover": True,
|
||||
"domain": "myq",
|
||||
},
|
||||
"AC02": {
|
||||
"always_discover": True,
|
||||
"domain": "tado",
|
||||
},
|
||||
"Abode": {
|
||||
"always_discover": True,
|
||||
"domain": "abode",
|
||||
},
|
||||
"BSB002": {
|
||||
"always_discover": False,
|
||||
"domain": "hue",
|
||||
},
|
||||
"C105X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"C135X": {
|
||||
"always_discover": True,
|
||||
"domain": "roku",
|
||||
},
|
||||
"EB-*": {
|
||||
"always_discover": True,
|
||||
"domain": "ecobee",
|
||||
},
|
||||
"Escea": {
|
||||
"always_discover": False,
|
||||
"domain": "escea",
|
||||
},
|
||||
"HHKBridge*": {
|
||||
"always_discover": True,
|
||||
"domain": "hive",
|
||||
},
|
||||
"Healty Home Coach": {
|
||||
"always_discover": True,
|
||||
"domain": "netatmo",
|
||||
},
|
||||
"Iota": {
|
||||
"always_discover": True,
|
||||
"domain": "abode",
|
||||
},
|
||||
"LIFX A19": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX BR30": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Beam": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Candle": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Clean": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Color": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX DLCOL": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX DLWW": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Dlight": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Downlight": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Filament": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX GU10": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Lightstrip": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Mini": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Nightvision": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Pls": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Plus": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Tile": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX White": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"LIFX Z": {
|
||||
"always_discover": True,
|
||||
"domain": "lifx",
|
||||
},
|
||||
"MYQ": {
|
||||
"always_discover": True,
|
||||
"domain": "myq",
|
||||
},
|
||||
"NL29": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"NL42": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"NL47": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"NL48": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"NL52": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"NL59": {
|
||||
"always_discover": False,
|
||||
"domain": "nanoleaf",
|
||||
},
|
||||
"Netatmo Relay": {
|
||||
"always_discover": True,
|
||||
"domain": "netatmo",
|
||||
},
|
||||
"PowerView": {
|
||||
"always_discover": True,
|
||||
"domain": "hunterdouglas_powerview",
|
||||
},
|
||||
"Presence": {
|
||||
"always_discover": True,
|
||||
"domain": "netatmo",
|
||||
},
|
||||
"Rachio": {
|
||||
"always_discover": True,
|
||||
"domain": "rachio",
|
||||
},
|
||||
"SPK5": {
|
||||
"always_discover": True,
|
||||
"domain": "rainmachine",
|
||||
},
|
||||
"Sensibo": {
|
||||
"always_discover": True,
|
||||
"domain": "sensibo",
|
||||
},
|
||||
"Smart Bridge": {
|
||||
"always_discover": False,
|
||||
"domain": "lutron_caseta",
|
||||
},
|
||||
"Socket": {
|
||||
"always_discover": False,
|
||||
"domain": "wemo",
|
||||
},
|
||||
"TRADFRI": {
|
||||
"always_discover": True,
|
||||
"domain": "tradfri",
|
||||
},
|
||||
"Touch HD": {
|
||||
"always_discover": True,
|
||||
"domain": "rainmachine",
|
||||
},
|
||||
"Welcome": {
|
||||
"always_discover": True,
|
||||
"domain": "netatmo",
|
||||
},
|
||||
"Wemo": {
|
||||
"always_discover": False,
|
||||
"domain": "wemo",
|
||||
},
|
||||
"YL*": {
|
||||
"always_discover": False,
|
||||
"domain": "yeelight",
|
||||
},
|
||||
"ecobee*": {
|
||||
"always_discover": True,
|
||||
"domain": "ecobee",
|
||||
},
|
||||
"iSmartGate": {
|
||||
"always_discover": True,
|
||||
"domain": "gogogate2",
|
||||
},
|
||||
"iZone": {
|
||||
"always_discover": True,
|
||||
"domain": "izone",
|
||||
},
|
||||
"tado": {
|
||||
"always_discover": True,
|
||||
"domain": "tado",
|
||||
},
|
||||
}
|
||||
|
||||
ZEROCONF = {
|
||||
|
|
|
@ -8,6 +8,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from collections.abc import Callable, Iterable
|
||||
from contextlib import suppress
|
||||
from dataclasses import dataclass
|
||||
import functools as ft
|
||||
import importlib
|
||||
import logging
|
||||
|
@ -118,6 +119,14 @@ class USBMatcher(USBMatcherRequired, USBMatcherOptional):
|
|||
"""Matcher for the bluetooth integration."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class HomeKitDiscoveredIntegration:
|
||||
"""HomeKit model."""
|
||||
|
||||
domain: str
|
||||
always_discover: bool
|
||||
|
||||
|
||||
class Manifest(TypedDict, total=False):
|
||||
"""Integration manifest.
|
||||
|
||||
|
@ -410,10 +419,30 @@ async def async_get_usb(hass: HomeAssistant) -> list[USBMatcher]:
|
|||
return usb
|
||||
|
||||
|
||||
async def async_get_homekit(hass: HomeAssistant) -> dict[str, str]:
|
||||
"""Return cached list of homekit models."""
|
||||
def homekit_always_discover(iot_class: str | None) -> bool:
|
||||
"""Return if we should always offer HomeKit control for a device."""
|
||||
#
|
||||
# Since we prefer local control, if the integration that is being
|
||||
# discovered is cloud AND the HomeKit device is UNPAIRED we still
|
||||
# want to discovery it.
|
||||
#
|
||||
# Additionally if the integration is polling, HKC offers a local
|
||||
# push experience for the user to control the device so we want
|
||||
# to offer that as well.
|
||||
#
|
||||
return not iot_class or (iot_class.startswith("cloud") or "polling" in iot_class)
|
||||
|
||||
homekit: dict[str, str] = HOMEKIT.copy()
|
||||
|
||||
async def async_get_homekit(
|
||||
hass: HomeAssistant,
|
||||
) -> dict[str, HomeKitDiscoveredIntegration]:
|
||||
"""Return cached list of homekit models."""
|
||||
homekit: dict[str, HomeKitDiscoveredIntegration] = {
|
||||
model: HomeKitDiscoveredIntegration(
|
||||
cast(str, details["domain"]), cast(bool, details["always_discover"])
|
||||
)
|
||||
for model, details in HOMEKIT.items()
|
||||
}
|
||||
|
||||
integrations = await async_get_custom_components(hass)
|
||||
for integration in integrations.values():
|
||||
|
@ -424,7 +453,10 @@ async def async_get_homekit(hass: HomeAssistant) -> dict[str, str]:
|
|||
):
|
||||
continue
|
||||
for model in integration.homekit["models"]:
|
||||
homekit[model] = integration.domain
|
||||
homekit[model] = HomeKitDiscoveredIntegration(
|
||||
integration.domain,
|
||||
homekit_always_discover(integration.iot_class),
|
||||
)
|
||||
|
||||
return homekit
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@ from __future__ import annotations
|
|||
|
||||
from collections import defaultdict
|
||||
|
||||
from homeassistant.loader import async_process_zeroconf_match_dict
|
||||
from homeassistant.loader import (
|
||||
async_process_zeroconf_match_dict,
|
||||
homekit_always_discover,
|
||||
)
|
||||
|
||||
from .model import Config, Integration
|
||||
from .serializer import format_python_namespace
|
||||
|
@ -12,7 +15,7 @@ from .serializer import format_python_namespace
|
|||
def generate_and_validate(integrations: dict[str, Integration]) -> str:
|
||||
"""Validate and generate zeroconf data."""
|
||||
service_type_dict = defaultdict(list)
|
||||
homekit_dict: dict[str, str] = {}
|
||||
homekit_dict: dict[str, dict[str, str]] = {}
|
||||
|
||||
for domain in sorted(integrations):
|
||||
integration = integrations[domain]
|
||||
|
@ -42,7 +45,12 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str:
|
|||
)
|
||||
break
|
||||
|
||||
homekit_dict[model] = domain
|
||||
homekit_dict[model] = {
|
||||
"domain": domain,
|
||||
"always_discover": homekit_always_discover(
|
||||
integration.manifest["iot_class"]
|
||||
),
|
||||
}
|
||||
|
||||
# HomeKit models are matched on starting string, make sure none overlap.
|
||||
warned = set()
|
||||
|
|
|
@ -542,7 +542,7 @@ async def test_homekit_match_partial_space(hass, mock_async_zeroconf):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"LIFX": "lifx"},
|
||||
{"LIFX": {"domain": "lifx", "always_discover": True}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -578,7 +578,7 @@ async def test_device_with_invalid_name(hass, mock_async_zeroconf, caplog):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"LIFX": "lifx"},
|
||||
{"LIFX": {"domain": "lifx", "always_discover": True}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -609,7 +609,7 @@ async def test_homekit_match_partial_dash(hass, mock_async_zeroconf):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"Smart Bridge": "lutron_caseta"},
|
||||
{"Smart Bridge": {"domain": "lutron_caseta", "always_discover": False}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -638,7 +638,11 @@ async def test_homekit_match_partial_fnmatch(hass, mock_async_zeroconf):
|
|||
zc_gen.ZEROCONF,
|
||||
{"_hap._tcp.local.": [{"domain": "homekit_controller"}]},
|
||||
clear=True,
|
||||
), patch.dict(zc_gen.HOMEKIT, {"YLDP*": "yeelight"}, clear=True), patch.object(
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"YLDP*": {"domain": "yeelight", "always_discover": False}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
) as mock_config_flow, patch.object(
|
||||
zeroconf,
|
||||
|
@ -667,7 +671,7 @@ async def test_homekit_match_full(hass, mock_async_zeroconf):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"BSB002": "hue"},
|
||||
{"BSB002": {"domain": "hue", "always_discover": False}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -698,7 +702,10 @@ async def test_homekit_already_paired(hass, mock_async_zeroconf):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"AC02": "tado", "tado": "tado"},
|
||||
{
|
||||
"AC02": {"domain": "tado", "always_discover": True},
|
||||
"tado": {"domain": "tado", "always_discover": True},
|
||||
},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -730,7 +737,7 @@ async def test_homekit_invalid_paring_status(hass, mock_async_zeroconf):
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"Smart Bridge": "lutron_caseta"},
|
||||
{"Smart Bridge": {"domain": "lutron_caseta", "always_discover": False}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -794,7 +801,7 @@ async def test_homekit_controller_still_discovered_unpaired_for_cloud(
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"Rachio": "rachio"},
|
||||
{"Rachio": {"domain": "rachio", "always_discover": True}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
@ -834,7 +841,7 @@ async def test_homekit_controller_still_discovered_unpaired_for_polling(
|
|||
clear=True,
|
||||
), patch.dict(
|
||||
zc_gen.HOMEKIT,
|
||||
{"iSmartGate": "gogogate2"},
|
||||
{"iSmartGate": {"domain": "gogogate2", "always_discover": True}},
|
||||
clear=True,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
|
|
|
@ -623,8 +623,8 @@ async def test_get_homekit(hass: HomeAssistant) -> None:
|
|||
"test_2": test_2_integration,
|
||||
}
|
||||
homekit = await loader.async_get_homekit(hass)
|
||||
assert homekit["test_1"] == "test_1"
|
||||
assert homekit["test_2"] == "test_2"
|
||||
assert homekit["test_1"] == loader.HomeKitDiscoveredIntegration("test_1", True)
|
||||
assert homekit["test_2"] == loader.HomeKitDiscoveredIntegration("test_2", True)
|
||||
|
||||
|
||||
async def test_get_ssdp(hass: HomeAssistant) -> None:
|
||||
|
|
Loading…
Reference in New Issue