mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Allow ZHA startup to fail instead of raising ConfigEntryNotReady
(#77417)
* Retry startup within ZHA instead of raising `ConfigEntryNotReady` * Add unit tests * Disable pylint warning for intentional broad except
This commit is contained in:
parent
795691038f
commit
2e8d598795
@ -408,3 +408,7 @@ class Strobe(t.enum8):
|
|||||||
|
|
||||||
No_Strobe = 0x00
|
No_Strobe = 0x00
|
||||||
Strobe = 0x01
|
Strobe = 0x01
|
||||||
|
|
||||||
|
|
||||||
|
STARTUP_FAILURE_DELAY_S = 3
|
||||||
|
STARTUP_RETRIES = 3
|
||||||
|
@ -13,7 +13,6 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
from typing import TYPE_CHECKING, Any, NamedTuple, Union
|
from typing import TYPE_CHECKING, Any, NamedTuple, Union
|
||||||
|
|
||||||
from serial import SerialException
|
|
||||||
from zigpy.application import ControllerApplication
|
from zigpy.application import ControllerApplication
|
||||||
from zigpy.config import CONF_DEVICE
|
from zigpy.config import CONF_DEVICE
|
||||||
import zigpy.device
|
import zigpy.device
|
||||||
@ -25,7 +24,6 @@ from homeassistant import __path__ as HOMEASSISTANT_PATH
|
|||||||
from homeassistant.components.system_log import LogEntry, _figure_out_source
|
from homeassistant.components.system_log import LogEntry, _figure_out_source
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
@ -62,6 +60,8 @@ from .const import (
|
|||||||
SIGNAL_ADD_ENTITIES,
|
SIGNAL_ADD_ENTITIES,
|
||||||
SIGNAL_GROUP_MEMBERSHIP_CHANGE,
|
SIGNAL_GROUP_MEMBERSHIP_CHANGE,
|
||||||
SIGNAL_REMOVE,
|
SIGNAL_REMOVE,
|
||||||
|
STARTUP_FAILURE_DELAY_S,
|
||||||
|
STARTUP_RETRIES,
|
||||||
UNKNOWN_MANUFACTURER,
|
UNKNOWN_MANUFACTURER,
|
||||||
UNKNOWN_MODEL,
|
UNKNOWN_MODEL,
|
||||||
ZHA_GW_MSG,
|
ZHA_GW_MSG,
|
||||||
@ -166,17 +166,27 @@ class ZHAGateway:
|
|||||||
app_config[CONF_DEVICE] = self.config_entry.data[CONF_DEVICE]
|
app_config[CONF_DEVICE] = self.config_entry.data[CONF_DEVICE]
|
||||||
|
|
||||||
app_config = app_controller_cls.SCHEMA(app_config)
|
app_config = app_controller_cls.SCHEMA(app_config)
|
||||||
try:
|
|
||||||
self.application_controller = await app_controller_cls.new(
|
for attempt in range(STARTUP_RETRIES):
|
||||||
app_config, auto_form=True, start_radio=True
|
try:
|
||||||
)
|
self.application_controller = await app_controller_cls.new(
|
||||||
except (asyncio.TimeoutError, SerialException, OSError) as exception:
|
app_config, auto_form=True, start_radio=True
|
||||||
_LOGGER.error(
|
)
|
||||||
"Couldn't start %s coordinator",
|
except Exception as exc: # pylint: disable=broad-except
|
||||||
self.radio_description,
|
_LOGGER.warning(
|
||||||
exc_info=exception,
|
"Couldn't start %s coordinator (attempt %s of %s)",
|
||||||
)
|
self.radio_description,
|
||||||
raise ConfigEntryNotReady from exception
|
attempt + 1,
|
||||||
|
STARTUP_RETRIES,
|
||||||
|
exc_info=exc,
|
||||||
|
)
|
||||||
|
|
||||||
|
if attempt == STARTUP_RETRIES - 1:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
await asyncio.sleep(STARTUP_FAILURE_DELAY_S)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
self.application_controller.add_listener(self)
|
self.application_controller.add_listener(self)
|
||||||
self.application_controller.groups.add_listener(self)
|
self.application_controller.groups.add_listener(self)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Test ZHA Gateway."""
|
"""Test ZHA Gateway."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import zigpy.profiles.zha as zha
|
import zigpy.profiles.zha as zha
|
||||||
@ -211,3 +211,51 @@ async def test_gateway_create_group_with_id(hass, device_light_1, coordinator):
|
|||||||
assert len(zha_group.members) == 1
|
assert len(zha_group.members) == 1
|
||||||
assert zha_group.members[0].device is device_light_1
|
assert zha_group.members[0].device is device_light_1
|
||||||
assert zha_group.group_id == 0x1234
|
assert zha_group.group_id == 0x1234
|
||||||
|
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"homeassistant.components.zha.core.gateway.ZHAGateway.async_load_devices",
|
||||||
|
MagicMock(),
|
||||||
|
)
|
||||||
|
@patch(
|
||||||
|
"homeassistant.components.zha.core.gateway.ZHAGateway.async_load_groups",
|
||||||
|
MagicMock(),
|
||||||
|
)
|
||||||
|
@patch("homeassistant.components.zha.core.gateway.STARTUP_FAILURE_DELAY_S", 0.01)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"startup",
|
||||||
|
[
|
||||||
|
[asyncio.TimeoutError(), FileNotFoundError(), MagicMock()],
|
||||||
|
[asyncio.TimeoutError(), MagicMock()],
|
||||||
|
[MagicMock()],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_gateway_initialize_success(startup, hass, device_light_1, coordinator):
|
||||||
|
"""Test ZHA initializing the gateway successfully."""
|
||||||
|
zha_gateway = get_zha_gateway(hass)
|
||||||
|
assert zha_gateway is not None
|
||||||
|
|
||||||
|
zha_gateway.shutdown = AsyncMock()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"bellows.zigbee.application.ControllerApplication.new", side_effect=startup
|
||||||
|
) as mock_new:
|
||||||
|
await zha_gateway.async_initialize()
|
||||||
|
|
||||||
|
assert mock_new.call_count == len(startup)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("homeassistant.components.zha.core.gateway.STARTUP_FAILURE_DELAY_S", 0.01)
|
||||||
|
async def test_gateway_initialize_failure(hass, device_light_1, coordinator):
|
||||||
|
"""Test ZHA failing to initialize the gateway."""
|
||||||
|
zha_gateway = get_zha_gateway(hass)
|
||||||
|
assert zha_gateway is not None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"bellows.zigbee.application.ControllerApplication.new",
|
||||||
|
side_effect=[asyncio.TimeoutError(), FileNotFoundError(), RuntimeError()],
|
||||||
|
) as mock_new:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
await zha_gateway.async_initialize()
|
||||||
|
|
||||||
|
assert mock_new.call_count == 3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user