Skip to content

Commit a815cb3

Browse files
feat: implement retry mechanism for linking to source device (#4577)
1 parent 10424a2 commit a815cb3

1 file changed

Lines changed: 65 additions & 36 deletions

File tree

custom_components/battery_notes/coordinator.py

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
STATE_UNAVAILABLE,
2020
STATE_UNKNOWN,
2121
)
22-
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
22+
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
2323
from homeassistant.helpers import (
2424
device_registry as dr,
2525
entity_registry as er,
2626
issue_registry as ir,
2727
)
2828
from homeassistant.helpers.entity_registry import RegistryEntry
29+
from homeassistant.helpers.event import async_call_later
2930
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
3031
from homeassistant.util import dt as dt_util
3132
from homeassistant.util.hass_dict import HassKey
@@ -127,6 +128,7 @@ class BatteryNotesSubentryCoordinator(DataUpdateCoordinator[None]):
127128
_previous_battery_low_binary_state: bool | None = None
128129
_source_entity_name: str | None = None
129130
_outlier_filter: LowOutlierFilter | None = None
131+
_link_retry_delay: timedelta = timedelta(minutes=2)
130132

131133
def __init__( # noqa: PLR0912
132134
self,
@@ -147,6 +149,7 @@ def __init__( # noqa: PLR0912
147149

148150
if not self._link_to_source():
149151
self.is_orphaned = True
152+
self._schedule_link_retry()
150153
return
151154

152155
self.battery_type = cast(str, self.subentry.data.get(CONF_BATTERY_TYPE, ""))
@@ -219,7 +222,31 @@ def __init__( # noqa: PLR0912
219222
)
220223
self.last_reported = last_reported
221224

222-
def _link_to_source(self) -> bool: # noqa: PLR0912
225+
def _schedule_link_retry(self) -> None:
226+
"""Retry linking to source after startup state settles."""
227+
228+
@callback
229+
def _retry_link_to_source(_now: datetime) -> None:
230+
if not self._link_to_source(create_issue=True):
231+
return
232+
233+
self.is_orphaned = False
234+
_LOGGER.debug(
235+
"%s source link restored; reloading entry to create entities",
236+
self.subentry.subentry_id,
237+
)
238+
self.hass.async_create_task(
239+
self.hass.config_entries.async_reload(self.config_entry.entry_id)
240+
)
241+
242+
remove_retry = async_call_later(
243+
self.hass,
244+
self._link_retry_delay,
245+
_retry_link_to_source,
246+
)
247+
self.config_entry.async_on_unload(remove_retry)
248+
249+
def _link_to_source(self, create_issue: bool = False) -> bool: # noqa: PLR0912
223250
"""Get the source device or entity, determine name and associate our wrapped battery if available."""
224251
device_registry = dr.async_get(self.hass)
225252
entity_registry = er.async_get(self.hass)
@@ -228,23 +255,24 @@ def _link_to_source(self) -> bool: # noqa: PLR0912
228255
entity = entity_registry.async_get(self.source_entity_id)
229256

230257
if not entity:
231-
ir.async_create_issue(
232-
self.hass,
233-
DOMAIN,
234-
f"missing_device_{self.subentry.subentry_id}",
235-
data={
236-
"entry_id": self.config_entry.entry_id,
237-
"subentry_id": self.subentry.subentry_id,
238-
"device_id": self.device_id,
239-
"source_entity_id": self.source_entity_id,
240-
},
241-
is_fixable=True,
242-
severity=ir.IssueSeverity.WARNING,
243-
translation_key="missing_device",
244-
translation_placeholders={
245-
"name": self.subentry.title,
246-
},
247-
)
258+
if create_issue:
259+
ir.async_create_issue(
260+
self.hass,
261+
DOMAIN,
262+
f"missing_device_{self.subentry.subentry_id}",
263+
data={
264+
"entry_id": self.config_entry.entry_id,
265+
"subentry_id": self.subentry.subentry_id,
266+
"device_id": self.device_id,
267+
"source_entity_id": self.source_entity_id,
268+
},
269+
is_fixable=True,
270+
severity=ir.IssueSeverity.WARNING,
271+
translation_key="missing_device",
272+
translation_placeholders={
273+
"name": self.subentry.title,
274+
},
275+
)
248276

249277
_LOGGER.warning(
250278
"%s is orphaned, unable to find entity %s",
@@ -314,23 +342,24 @@ def _link_to_source(self) -> bool: # noqa: PLR0912
314342
else:
315343
self.device_name = self.subentry.title
316344

317-
ir.async_create_issue(
318-
self.hass,
319-
DOMAIN,
320-
f"missing_device_{self.subentry.subentry_id}",
321-
data={
322-
"entry_id": self.config_entry.entry_id,
323-
"subentry_id": self.subentry.subentry_id,
324-
"device_id": self.device_id,
325-
"source_entity_id": self.source_entity_id,
326-
},
327-
is_fixable=True,
328-
severity=ir.IssueSeverity.WARNING,
329-
translation_key="missing_device",
330-
translation_placeholders={
331-
"name": self.subentry.title,
332-
},
333-
)
345+
if create_issue:
346+
ir.async_create_issue(
347+
self.hass,
348+
DOMAIN,
349+
f"missing_device_{self.subentry.subentry_id}",
350+
data={
351+
"entry_id": self.config_entry.entry_id,
352+
"subentry_id": self.subentry.subentry_id,
353+
"device_id": self.device_id,
354+
"source_entity_id": self.source_entity_id,
355+
},
356+
is_fixable=True,
357+
severity=ir.IssueSeverity.WARNING,
358+
translation_key="missing_device",
359+
translation_placeholders={
360+
"name": self.subentry.title,
361+
},
362+
)
334363

335364
_LOGGER.warning(
336365
"%s is orphaned, unable to find device %s",

0 commit comments

Comments
 (0)