Fix produce-box select cancelling config entry setup
Validate / hassfest (push) Skipped
Validate / pytest (push) Successful in 11s
Validate / HACS (push) Skipped

async_added_to_hass listed the switchable boxes inline, one fetch per box
popup, so on a slower connection the platform setup ran past
SLOW_SETUP_MAX_WAIT and the whole config entry was cancelled into
setup_error — every entity unavailable despite valid credentials. Move the
listing to a background task so setup never blocks on it; the options fill in
once it returns.

Also bound every portal request to a 30s timeout so one hung request can no
longer drag a refresh, or a first setup, past Home Assistant's own limits and
fail it outright.

Regression covered in tests/test_setup_hygiene.py.
This commit is contained in:
flan
2026-08-04 16:04:27 +00:00
parent 8051cc7465
commit 0a5441779a
9 changed files with 152 additions and 19 deletions
+17 -3
View File
@@ -20,6 +20,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import FreshHarvestConfigEntry
from .api import FreshHarvestError
from .const import DOMAIN
from .coordinator import FreshHarvestCoordinator
from .entity import FreshHarvestEntity
@@ -79,13 +80,26 @@ class FreshHarvestBoxSelect(FreshHarvestEntity, SelectEntity):
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()
# Listing costs a fetch per box popup, so it happens once on setup
# rather than on every coordinator refresh.
# Listing the boxes on offer costs one fetch per box popup. Doing it
# here inline blocked entity setup: on a slower connection those fetches
# ran past Home Assistant's SLOW_SETUP_MAX_WAIT and the whole config
# entry was cancelled into "setup_error", leaving every entity
# unavailable despite valid credentials. It now runs once, in the
# background — the entity comes up immediately with the current box as
# its only option and the rest appear when the listing returns.
self.coordinator.config_entry.async_create_background_task(
self.hass, self._async_load_options(), f"{DOMAIN}_list_baskets"
)
async def _async_load_options(self) -> None:
"""Fetch the switchable boxes once and publish them as options."""
try:
baskets = await self.coordinator.actions.async_list_baskets()
self._options = [b.name for b in baskets]
except FreshHarvestError as err:
_LOGGER.warning("could not list produce boxes: %s", err)
return
self._options = [b.name for b in baskets]
self.async_write_ha_state()
async def async_select_option(self, option: str) -> None:
"""Switch the next delivery to this box."""