From 45941f66c0fbb53fd5544f9621320e9f21c152e2 Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Sat, 5 Sep 2026 14:13:03 -0600 Subject: [PATCH 1/7] Add connect timeout to anthemav config entry setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit anthemav.Connection.create() retries the initial TCP connection internally (with its own exponential backoff, capped at 300s between attempts) and only returns once it succeeds, so it never raises OSError on its own when the receiver is unreachable/powered off. Nothing bounds that wait in async_setup_entry today, so setup blocks indefinitely. At startup this is worse than just a slow boot for this one entry: the only thing that ever stops it is Home Assistant's own global bootstrap stage-2 timeout (~5 minutes), and when that fires it force-cancels whatever other config entries are still mid-setup alongside it too — so one anthemav receiver being off at boot can take down unrelated integrations that would otherwise have set up fine, with no error logged against them individually (only anthemav's own cancellation is logged, from bootstrap's perspective it's the one entry it was still explicitly waiting on). Wrap the connection attempt in asyncio.timeout(CONNECT_TIMEOUT_SECONDS) and treat TimeoutError the same as OSError/DeviceError: raise ConfigEntryNotReady so Home Assistant's normal per-entry retry/backoff takes over instead. This is the same pattern already used by several other integrations with a similar "connect once during setup" shape (matter, cambridge_audio, music_assistant, zwave_js, hue, lutron_caseta, ...). 10s was chosen as generous for a LAN TCP connect while still well inside a boot that won't be noticeably slower when the receiver is reachable. Added a test (test_config_entry_not_ready_when_connect_hangs) covering the new timeout path, alongside the existing test_config_entry_not_ready_when_oserror. --- homeassistant/components/anthemav/__init__.py | 34 ++++++++++++++----- homeassistant/components/anthemav/const.py | 8 +++++ tests/components/anthemav/test_init.py | 29 ++++++++++++++++ 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index 8d14dfbf3ac09b..cfa31ef6d0c16d 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -1,5 +1,6 @@ """The Anthem A/V Receivers integration.""" +import asyncio import logging import anthemav @@ -20,7 +21,13 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_send -from .const import ANTHEMAV_UPDATE_SIGNAL, DEVICE_TIMEOUT_SECONDS, DOMAIN, MANUFACTURER +from .const import ( + ANTHEMAV_UPDATE_SIGNAL, + CONNECT_TIMEOUT_SECONDS, + DEVICE_TIMEOUT_SECONDS, + DOMAIN, + MANUFACTURER, +) type AnthemavConfigEntry = ConfigEntry[anthemav.Connection] @@ -39,16 +46,27 @@ def async_anthemav_update_callback(message: str) -> None: async_dispatcher_send(hass, f"{ANTHEMAV_UPDATE_SIGNAL}_{entry.entry_id}") try: - avr = await anthemav.Connection.create( - host=entry.data[CONF_HOST], - port=entry.data[CONF_PORT], - update_callback=async_anthemav_update_callback, - ) + # anthemav.Connection.create() retries the initial connection + # internally with its own backoff and only returns once it + # succeeds, so it will not raise OSError on its own when the + # receiver is unreachable. Bound the attempt so an unreachable + # receiver fails fast into the normal ConfigEntryNotReady retry + # path, rather than blocking setup (and, at startup, Home + # Assistant's bootstrap) for as long as the receiver stays off. + async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): + avr = await anthemav.Connection.create( + host=entry.data[CONF_HOST], + port=entry.data[CONF_PORT], + update_callback=async_anthemav_update_callback, + ) # Wait for the zones to be initialised based on the model await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) - except (OSError, DeviceError) as err: - raise ConfigEntryNotReady from err + except (OSError, DeviceError, TimeoutError) as err: + raise ConfigEntryNotReady( + f"Unable to connect to Anthem AVR at {entry.data[CONF_HOST]}:" + f"{entry.data[CONF_PORT]}" + ) from err entry.runtime_data = avr diff --git a/homeassistant/components/anthemav/const.py b/homeassistant/components/anthemav/const.py index 8bcdd013a63e18..6d6937eb069773 100644 --- a/homeassistant/components/anthemav/const.py +++ b/homeassistant/components/anthemav/const.py @@ -7,3 +7,11 @@ DOMAIN = "anthemav" MANUFACTURER = "Anthem" DEVICE_TIMEOUT_SECONDS = 4.0 +# anthemav.Connection.create() retries its initial connection attempt +# internally (with exponential backoff) and only returns once it succeeds, +# so it does not fail on its own when the receiver is unreachable. Bound it +# here instead of relying on Home Assistant's global bootstrap timeout, +# which would otherwise let one unreachable-at-boot receiver block startup +# for minutes and can collaterally cancel other integrations still setting +# up alongside it. +CONNECT_TIMEOUT_SECONDS = 10.0 diff --git a/tests/components/anthemav/test_init.py b/tests/components/anthemav/test_init.py index 27a32bacff5cfd..b1a31af8686c4b 100644 --- a/tests/components/anthemav/test_init.py +++ b/tests/components/anthemav/test_init.py @@ -1,5 +1,6 @@ """Test the Anthem A/V Receivers config flow.""" +import asyncio from collections.abc import Callable from unittest.mock import ANY, AsyncMock, patch @@ -72,6 +73,34 @@ async def test_config_entry_not_ready_when_oserror( assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY +async def test_config_entry_not_ready_when_connect_hangs( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +) -> None: + """Test setup fails fast (instead of hanging) when the AVR never connects. + + anthemav.Connection.create() retries its initial connection internally + and only returns once it succeeds, so it never raises OSError on its + own when the receiver is unreachable — nothing bounds that wait except + our own timeout. Simulate that by having the mocked create() hang + indefinitely, and confirm setup still resolves to SETUP_RETRY rather + than blocking forever. + """ + with ( + patch( + "homeassistant.components.anthemav.CONNECT_TIMEOUT_SECONDS", + 0.01, + ), + patch( + "anthemav.Connection.create", + side_effect=lambda *args, **kwargs: asyncio.sleep(3600), + ), + ): + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + async def test_anthemav_dispatcher_signal( hass: HomeAssistant, mock_connection_create: AsyncMock, From d956c223c1b20f052062054db5ec317cece5488f Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Sat, 5 Sep 2026 14:33:15 -0600 Subject: [PATCH 2/7] Fix test's mocked hang to actually await (per Copilot review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The side_effect was a plain lambda returning asyncio.sleep(3600) — since AsyncMock calls that synchronously and awaits its return value, the sleep() coroutine was returned but never awaited, so the mock resolved immediately without actually exercising the timeout path. Use an async function instead so the sleep is genuinely awaited. Verified in isolation (see PR comment) that the original version returns instantly while the fixed version now correctly triggers asyncio.timeout()'s TimeoutError. --- tests/components/anthemav/test_init.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/components/anthemav/test_init.py b/tests/components/anthemav/test_init.py index b1a31af8686c4b..0d8ed9c291fd1f 100644 --- a/tests/components/anthemav/test_init.py +++ b/tests/components/anthemav/test_init.py @@ -85,6 +85,10 @@ async def test_config_entry_not_ready_when_connect_hangs( indefinitely, and confirm setup still resolves to SETUP_RETRY rather than blocking forever. """ + async def _hang(*args, **kwargs) -> None: + """Simulate Connection.create() never returning.""" + await asyncio.sleep(3600) + with ( patch( "homeassistant.components.anthemav.CONNECT_TIMEOUT_SECONDS", @@ -92,7 +96,7 @@ async def test_config_entry_not_ready_when_connect_hangs( ), patch( "anthemav.Connection.create", - side_effect=lambda *args, **kwargs: asyncio.sleep(3600), + side_effect=_hang, ), ): mock_config_entry.add_to_hass(hass) From 15008ff2d41a981642ceb8510690e314ef6580fe Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Sat, 5 Sep 2026 14:34:06 -0600 Subject: [PATCH 3/7] Trim duplicated rationale comments (per Copilot review) The same explanation of why CONNECT_TIMEOUT_SECONDS exists was repeated across the implementation, the constant, and the test docstring. Keep it in one place (the constant) and just point there from the call site; trim the test docstring to its behavior contract. --- homeassistant/components/anthemav/__init__.py | 8 +------- homeassistant/components/anthemav/const.py | 9 ++------- tests/components/anthemav/test_init.py | 12 ++---------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index cfa31ef6d0c16d..e18d3661be2a3c 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -46,13 +46,7 @@ def async_anthemav_update_callback(message: str) -> None: async_dispatcher_send(hass, f"{ANTHEMAV_UPDATE_SIGNAL}_{entry.entry_id}") try: - # anthemav.Connection.create() retries the initial connection - # internally with its own backoff and only returns once it - # succeeds, so it will not raise OSError on its own when the - # receiver is unreachable. Bound the attempt so an unreachable - # receiver fails fast into the normal ConfigEntryNotReady retry - # path, rather than blocking setup (and, at startup, Home - # Assistant's bootstrap) for as long as the receiver stays off. + # See CONNECT_TIMEOUT_SECONDS for why this needs a timeout. async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): avr = await anthemav.Connection.create( host=entry.data[CONF_HOST], diff --git a/homeassistant/components/anthemav/const.py b/homeassistant/components/anthemav/const.py index 6d6937eb069773..fcefd757f31117 100644 --- a/homeassistant/components/anthemav/const.py +++ b/homeassistant/components/anthemav/const.py @@ -7,11 +7,6 @@ DOMAIN = "anthemav" MANUFACTURER = "Anthem" DEVICE_TIMEOUT_SECONDS = 4.0 -# anthemav.Connection.create() retries its initial connection attempt -# internally (with exponential backoff) and only returns once it succeeds, -# so it does not fail on its own when the receiver is unreachable. Bound it -# here instead of relying on Home Assistant's global bootstrap timeout, -# which would otherwise let one unreachable-at-boot receiver block startup -# for minutes and can collaterally cancel other integrations still setting -# up alongside it. +# anthemav.Connection.create() retries internally and only returns once +# connected, so it never fails on its own when the receiver is unreachable. CONNECT_TIMEOUT_SECONDS = 10.0 diff --git a/tests/components/anthemav/test_init.py b/tests/components/anthemav/test_init.py index 0d8ed9c291fd1f..6b05f92253e32d 100644 --- a/tests/components/anthemav/test_init.py +++ b/tests/components/anthemav/test_init.py @@ -76,17 +76,9 @@ async def test_config_entry_not_ready_when_oserror( async def test_config_entry_not_ready_when_connect_hangs( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: - """Test setup fails fast (instead of hanging) when the AVR never connects. - - anthemav.Connection.create() retries its initial connection internally - and only returns once it succeeds, so it never raises OSError on its - own when the receiver is unreachable — nothing bounds that wait except - our own timeout. Simulate that by having the mocked create() hang - indefinitely, and confirm setup still resolves to SETUP_RETRY rather - than blocking forever. - """ + """Test setup fails fast (instead of hanging) when the AVR never connects.""" + async def _hang(*args, **kwargs) -> None: - """Simulate Connection.create() never returning.""" await asyncio.sleep(3600) with ( From ff850ced0d7060c157d2020e4c749bdcb485ec24 Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Tue, 8 Sep 2026 13:46:39 -0600 Subject: [PATCH 4/7] Don't attach the connect-failure message to DeviceError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceError comes from wait_for_device_initialised(), which only runs after Connection.create() has already succeeded — the TCP connection is fine, the receiver just didn't report its model/MAC in time. The shared "Unable to connect to Anthem AVR at host:port" message was misleading for that case, pointing at the network for a problem that isn't there. Split it: TimeoutError (only raisable by our own asyncio.timeout() around Connection.create() — wait_for_device_initialised() converts its own internal timeout into DeviceError, so a bare TimeoutError from that call is not reachable here) gets the connect-specific message; OSError/DeviceError keep the original unqualified ConfigEntryNotReady. Per review feedback from @blues-sechseck. --- homeassistant/components/anthemav/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index e18d3661be2a3c..a473da8a4448e0 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -56,11 +56,18 @@ def async_anthemav_update_callback(message: str) -> None: # Wait for the zones to be initialised based on the model await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) - except (OSError, DeviceError, TimeoutError) as err: + except TimeoutError as err: + # Only our own asyncio.timeout() above can raise this — the TCP + # connection itself never completed. wait_for_device_initialised() + # converts its own internal timeout into DeviceError instead, so by + # the time that call is reached the connection has already + # succeeded and this branch can't be it. raise ConfigEntryNotReady( - f"Unable to connect to Anthem AVR at {entry.data[CONF_HOST]}:" - f"{entry.data[CONF_PORT]}" + f"Timed out connecting to Anthem AVR at " + f"{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}" ) from err + except (OSError, DeviceError) as err: + raise ConfigEntryNotReady from err entry.runtime_data = avr From b626bf1b660a5f5796c7c35f2f7bf221503f3e04 Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Tue, 8 Sep 2026 16:51:42 -0600 Subject: [PATCH 5/7] Condense TimeoutError comment (per Copilot review) The exception handler and the surrounding except block ordering already communicate why this branch exists; the five-line narration duplicated that and hard-coded aiophyn/anthemav internals that could go stale. --- homeassistant/components/anthemav/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index a473da8a4448e0..9d84ca07e60856 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -57,11 +57,7 @@ def async_anthemav_update_callback(message: str) -> None: # Wait for the zones to be initialised based on the model await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) except TimeoutError as err: - # Only our own asyncio.timeout() above can raise this — the TCP - # connection itself never completed. wait_for_device_initialised() - # converts its own internal timeout into DeviceError instead, so by - # the time that call is reached the connection has already - # succeeded and this branch can't be it. + # Raised only by the asyncio.timeout() above; the connection never completed. raise ConfigEntryNotReady( f"Timed out connecting to Anthem AVR at " f"{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}" From 2cb5273717961b5e799a1b86f29902d615079968 Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Thu, 10 Sep 2026 17:57:40 -0600 Subject: [PATCH 6/7] Scope the connect-timeout handler to Connection.create() only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous except TimeoutError sat at the outer try level, so it would also catch a TimeoutError from wait_for_device_initialised() after the connection already succeeded — misreporting a device-init timeout as "Timed out connecting" when the network was never the problem. Nest the timeout handling so it only wraps the asyncio.timeout() block around Connection.create(). TimeoutError (itself an OSError subclass) from anywhere else still falls through to the existing generic except (OSError, DeviceError) path, just without the misleading connect-specific message. Added a regression test: forces wait_for_device_initialised() to raise TimeoutError post-connect and asserts the connect-specific message is not used. Verified it fails against the prior (unscoped) code and passes against this fix. Per Copilot review feedback (flagged twice, on two different commits). --- homeassistant/components/anthemav/__init__.py | 26 +++++++++---------- tests/components/anthemav/test_init.py | 20 ++++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index 9d84ca07e60856..89f7c112e7d654 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -46,22 +46,22 @@ def async_anthemav_update_callback(message: str) -> None: async_dispatcher_send(hass, f"{ANTHEMAV_UPDATE_SIGNAL}_{entry.entry_id}") try: - # See CONNECT_TIMEOUT_SECONDS for why this needs a timeout. - async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): - avr = await anthemav.Connection.create( - host=entry.data[CONF_HOST], - port=entry.data[CONF_PORT], - update_callback=async_anthemav_update_callback, - ) + try: + # See CONNECT_TIMEOUT_SECONDS for why this needs a timeout. + async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): + avr = await anthemav.Connection.create( + host=entry.data[CONF_HOST], + port=entry.data[CONF_PORT], + update_callback=async_anthemav_update_callback, + ) + except TimeoutError as err: + raise ConfigEntryNotReady( + f"Timed out connecting to Anthem AVR at " + f"{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}" + ) from err # Wait for the zones to be initialised based on the model await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) - except TimeoutError as err: - # Raised only by the asyncio.timeout() above; the connection never completed. - raise ConfigEntryNotReady( - f"Timed out connecting to Anthem AVR at " - f"{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}" - ) from err except (OSError, DeviceError) as err: raise ConfigEntryNotReady from err diff --git a/tests/components/anthemav/test_init.py b/tests/components/anthemav/test_init.py index 6b05f92253e32d..51b587f1a50bca 100644 --- a/tests/components/anthemav/test_init.py +++ b/tests/components/anthemav/test_init.py @@ -97,6 +97,26 @@ async def _hang(*args, **kwargs) -> None: assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY +async def test_device_init_timeout_not_reported_as_connect_timeout( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_anthemav: AsyncMock, +) -> None: + """Test a post-connect TimeoutError isn't misreported as a connect timeout.""" + mock_anthemav.protocol.wait_for_device_initialised = AsyncMock( + side_effect=TimeoutError + ) + with patch("anthemav.Connection.create", return_value=mock_anthemav): + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + assert ( + mock_config_entry.reason + != "Timed out connecting to Anthem AVR at 1.1.1.1:14999" + ) + + async def test_anthemav_dispatcher_signal( hass: HomeAssistant, mock_connection_create: AsyncMock, From 2c53bc58fcec3d0d99ff06dd79d29f21e3a55a30 Mon Sep 17 00:00:00 2001 From: luck-y13 Date: Fri, 11 Sep 2026 14:07:12 -0600 Subject: [PATCH 7/7] Flatten to a single try block; patch at point of use in test Per @joostlek review: - __init__.py: replace the nested try with a single try and an `avr` sentinel (None until Connection.create() succeeds). The except TimeoutError branch checks the sentinel to decide whether the connect-specific message applies, instead of relying on nesting to scope which statement failed. Same behavior, one try block. - test_init.py: patch wait_for_device_initialised via patch.object() in the `with` block alongside the Connection.create patch, instead of mutating the fixture's mock_anthemav.protocol attribute directly before the `with`. Re-verified: ruff check/format clean, mypy clean for anthemav, full tests/components/anthemav/ suite (13 tests) still passes, including the regression test for the DeviceError-timeout misclassification. Co-Authored-By: Claude Sonnet 5 --- homeassistant/components/anthemav/__init__.py | 26 ++++++++++--------- tests/components/anthemav/test_init.py | 12 ++++++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/anthemav/__init__.py b/homeassistant/components/anthemav/__init__.py index 89f7c112e7d654..53fa033273d060 100644 --- a/homeassistant/components/anthemav/__init__.py +++ b/homeassistant/components/anthemav/__init__.py @@ -45,23 +45,25 @@ def async_anthemav_update_callback(message: str) -> None: _LOGGER.debug("Received update callback from AVR: %s", message) async_dispatcher_send(hass, f"{ANTHEMAV_UPDATE_SIGNAL}_{entry.entry_id}") + avr: anthemav.Connection | None = None try: - try: - # See CONNECT_TIMEOUT_SECONDS for why this needs a timeout. - async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): - avr = await anthemav.Connection.create( - host=entry.data[CONF_HOST], - port=entry.data[CONF_PORT], - update_callback=async_anthemav_update_callback, - ) - except TimeoutError as err: + # See CONNECT_TIMEOUT_SECONDS for why this needs a timeout. + async with asyncio.timeout(CONNECT_TIMEOUT_SECONDS): + avr = await anthemav.Connection.create( + host=entry.data[CONF_HOST], + port=entry.data[CONF_PORT], + update_callback=async_anthemav_update_callback, + ) + + # Wait for the zones to be initialised based on the model + await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) + except TimeoutError as err: + if avr is None: raise ConfigEntryNotReady( f"Timed out connecting to Anthem AVR at " f"{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}" ) from err - - # Wait for the zones to be initialised based on the model - await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS) + raise ConfigEntryNotReady from err except (OSError, DeviceError) as err: raise ConfigEntryNotReady from err diff --git a/tests/components/anthemav/test_init.py b/tests/components/anthemav/test_init.py index 51b587f1a50bca..dfae1ddf04b405 100644 --- a/tests/components/anthemav/test_init.py +++ b/tests/components/anthemav/test_init.py @@ -103,10 +103,14 @@ async def test_device_init_timeout_not_reported_as_connect_timeout( mock_anthemav: AsyncMock, ) -> None: """Test a post-connect TimeoutError isn't misreported as a connect timeout.""" - mock_anthemav.protocol.wait_for_device_initialised = AsyncMock( - side_effect=TimeoutError - ) - with patch("anthemav.Connection.create", return_value=mock_anthemav): + with ( + patch("anthemav.Connection.create", return_value=mock_anthemav), + patch.object( + mock_anthemav.protocol, + "wait_for_device_initialised", + side_effect=TimeoutError, + ), + ): mock_config_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done()