Pre-filter zeroconf service browser updates (#35518)
Each ServerBrowser currently runs in its own thread which processes every A or AAAA record update per instance. As the list of zeroconf names we watch for grows, each additional ServiceBrowser would process all the A and AAAA updates on the network. To avoid overwhemling the system we pre-filter here and only process DNSPointers for the configured record name (type)pull/35523/head
parent
dd22200a69
commit
3315c4c6c3
|
@ -5,6 +5,8 @@ import socket
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from zeroconf import (
|
from zeroconf import (
|
||||||
|
DNSPointer,
|
||||||
|
DNSRecord,
|
||||||
InterfaceChoice,
|
InterfaceChoice,
|
||||||
NonUniqueNameException,
|
NonUniqueNameException,
|
||||||
ServiceBrowser,
|
ServiceBrowser,
|
||||||
|
@ -75,6 +77,27 @@ def _get_instance(hass, default_interface=False):
|
||||||
return zeroconf
|
return zeroconf
|
||||||
|
|
||||||
|
|
||||||
|
class HaServiceBrowser(ServiceBrowser):
|
||||||
|
"""ServiceBrowser that only consumes DNSPointer records."""
|
||||||
|
|
||||||
|
def update_record(self, zc: "Zeroconf", now: float, record: DNSRecord) -> None:
|
||||||
|
"""Pre-Filter update_record to DNSPointers for the configured type."""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Each ServerBrowser currently runs in its own thread which
|
||||||
|
# processes every A or AAAA record update per instance.
|
||||||
|
#
|
||||||
|
# As the list of zeroconf names we watch for grows, each additional
|
||||||
|
# ServiceBrowser would process all the A and AAAA updates on the network.
|
||||||
|
#
|
||||||
|
# To avoid overwhemling the system we pre-filter here and only process
|
||||||
|
# DNSPointers for the configured record name (type)
|
||||||
|
#
|
||||||
|
if record.name != self.type or not isinstance(record, DNSPointer):
|
||||||
|
return
|
||||||
|
super().update_record(zc, now, record)
|
||||||
|
|
||||||
|
|
||||||
class HaZeroconf(Zeroconf):
|
class HaZeroconf(Zeroconf):
|
||||||
"""Zeroconf that cannot be closed."""
|
"""Zeroconf that cannot be closed."""
|
||||||
|
|
||||||
|
@ -166,10 +189,10 @@ def setup(hass, config):
|
||||||
)
|
)
|
||||||
|
|
||||||
for service in ZEROCONF:
|
for service in ZEROCONF:
|
||||||
ServiceBrowser(zeroconf, service, handlers=[service_update])
|
HaServiceBrowser(zeroconf, service, handlers=[service_update])
|
||||||
|
|
||||||
if HOMEKIT_TYPE not in ZEROCONF:
|
if HOMEKIT_TYPE not in ZEROCONF:
|
||||||
ServiceBrowser(zeroconf, HOMEKIT_TYPE, handlers=[service_update])
|
HaServiceBrowser(zeroconf, HOMEKIT_TYPE, handlers=[service_update])
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ async def test_setup(hass, mock_zeroconf):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser:
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
|
@ -87,7 +87,7 @@ async def test_setup(hass, mock_zeroconf):
|
||||||
async def test_setup_with_default_interface(hass, mock_zeroconf):
|
async def test_setup_with_default_interface(hass, mock_zeroconf):
|
||||||
"""Test default interface config."""
|
"""Test default interface config."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
|
@ -100,7 +100,7 @@ async def test_setup_with_default_interface(hass, mock_zeroconf):
|
||||||
async def test_setup_without_default_interface(hass, mock_zeroconf):
|
async def test_setup_without_default_interface(hass, mock_zeroconf):
|
||||||
"""Test without default interface config."""
|
"""Test without default interface config."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
|
@ -117,7 +117,7 @@ async def test_homekit_match_partial_space(hass, mock_zeroconf):
|
||||||
), patch.object(
|
), patch.object(
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser:
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock("LIFX bulb")
|
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock("LIFX bulb")
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
|
@ -134,7 +134,7 @@ async def test_homekit_match_partial_dash(hass, mock_zeroconf):
|
||||||
), patch.object(
|
), patch.object(
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser:
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
||||||
"Rachio-fa46ba"
|
"Rachio-fa46ba"
|
||||||
|
@ -153,7 +153,7 @@ async def test_homekit_match_full(hass, mock_zeroconf):
|
||||||
), patch.object(
|
), patch.object(
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "ServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser:
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock("BSB002")
|
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock("BSB002")
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
|
|
Loading…
Reference in New Issue