Skip to content

Commit f6d2da2

Browse files
authored
Document how to handle integration setup failures (home-assistant#1360)
1 parent 68c88d3 commit f6d2da2

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

docs/integration_setup_failures.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: "Handling Setup Failures"
3+
---
4+
5+
Your integration may not be able to be set up for a variety of reasons. The most common cases are because the device or service is offline or the credentials are no longer valid. Your integration must retry setup so it can recover as soon as reasonably possible when the device or service is back online without the user having to restart Home Assistant.
6+
7+
## Handling offline or unavailable devices and services
8+
9+
### Integrations using `async_setup_entry`
10+
11+
Raise the `ConfigEntryNotReady` exception from `async_setup_entry` in the integration's `__init__.py`, and Home Assistant will automatically take care of retrying set up later. To avoid doubt, raising `ConfigEntryNotReady` in a platform's `async_setup_entry` is ineffective because it is too late to be caught by the config entry setup.
12+
13+
#### Example
14+
15+
```python
16+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
17+
"""Setup the config entry for my device."""
18+
device = MyDevice(entry.data[CONF_HOST])
19+
try:
20+
await device.async_setup()
21+
except (asyncio.TimeoutError, TimeoutException) as ex:
22+
raise ConfigEntryNotReady(f"Timeout while connecting to {device.ipaddr}") from ex
23+
```
24+
25+
If you are using a [DataUpdateCoordinator](integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities), calling `await coordinator.async_config_entry_first_refresh()` will also trigger this exception automaticlly if the first refresh failed.
26+
27+
If your integration supports discovery, Home Assistant will automatically retry as soon as your device or service gets discovered.
28+
29+
#### Handling logging of a retry
30+
31+
Pass the error message to `ConfigEntryNotReady` as the first argument. Home Assistant will log the retry once with a log level of
32+
`warning`, and subsequent retires are logged at `debug` level. The error message will also be propagated to the UI and shown on the integrations page. Suppose you do not set a message when raising `ConfigEntryNotReady`; in that case, Home Assistant will try to extract the reason from the exception that is the cause of `ConfigEntryNotReady` if it was propagated from another exception.
33+
34+
The integration should not log any non-debug messages about the retry, and should instead rely on the logic built-in to `ConfigEntryNotReady` to avoid spamming the logs.
35+
36+
### Integrations using `async_setup_platform`
37+
38+
Raise the `PlatformNotReady` exception from `async_setup_platform`, and Home Assistant will automatically take care of retrying set up later.
39+
40+
#### Example
41+
42+
```python
43+
async def async_setup_platform(
44+
hass: HomeAssistant,
45+
config: ConfigType,
46+
async_add_entities: AddEntitiesCallback,
47+
discovery_info: DiscoveryInfoType | None = None,
48+
) -> None:
49+
"""Set up the platform."""
50+
device = MyDevice(conf[CONF_HOST])
51+
try:
52+
await device.async_setup()
53+
except ConnectionError as ex:
54+
raise PlatformNotReady(f"Connection error while connecting to {device.ipaddr}: {ex}") from ex
55+
```
56+
57+
#### Handling logging of a retry
58+
59+
Pass the error message to `PlatformNotReady` as the first argument. Home Assistant will log the retry once with a log level of
60+
`warning`, and subsequent retires will be logged at `debug` level. Suppose you do not set a message when raising `ConfigEntryNotReady`; in that case, Home Assistant will try to extract the reason from the exception that is the cause of `ConfigEntryNotReady` if it was propagated from another exception.
61+
62+
The integration should not log any non-debug messages about the retry, and should instead rely on the logic built-in to `PlatformNotReady` to avoid spamming the logs.
63+
64+
## Handling expired credentials
65+
66+
Raise the `ConfigEntryAuthFailed` exception, and Home Assistant will automatically put the config entry in a failure state and start a reauth flow. The exception must be raised from `async_setup_entry` in `__init__.py` or from the `DataUpdateCoordinator` or the exception will not be effective at triggering the reauth flow. If your integration does not use a `DataUpdateCoordinator`, calling `entry.async_start_reauth()` can be used as an alternative to starting a reauth flow.
67+
68+
The `reauth` flow will be started with the following context variables, which are available in the `async_step_reauth` step:
69+
70+
- source: This will always be "SOURCE_REAUTH"
71+
- entry_id: The entry_id of the config entry that needs reauthentication
72+
- unique_id: The unique_id of the config entry that needs reauthentication
73+
74+
#### Example
75+
76+
```python
77+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
78+
"""Setup the config entry for my device."""
79+
device = MyDevice(entry.data[CONF_HOST])
80+
try:
81+
await device.async_setup()
82+
except AuthFailed as ex:
83+
raise ConfigEntryAuthFailed(f"Credentials expired for {device.name}") from ex
84+
except (asyncio.TimeoutError, TimeoutException) as ex:
85+
raise ConfigEntryNotReady(f"Timed out while connecting to {device.ipaddr}") from ex
86+
```

sidebars.js

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ module.exports = {
135135
"creating_platform_index",
136136
"creating_component_generic_discovery",
137137
"integration_fetching_data",
138+
"integration_setup_failures",
138139
"integration_events",
139140
"integration_listen_events",
140141
"network_discovery",

0 commit comments

Comments
 (0)