Skip to content

Commit 5ed03f1

Browse files
committed
chore: update repairs
1 parent 7643d8b commit 5ed03f1

File tree

29 files changed

+342
-10226
lines changed

29 files changed

+342
-10226
lines changed

custom_components/action_result/__init__.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from homeassistant.const import Platform
2727
from homeassistant.core import Event, EventStateChangedData, callback
2828
from homeassistant.exceptions import ConfigEntryNotReady
29+
from homeassistant.helpers import issue_registry as ir
2930
import homeassistant.helpers.config_validation as cv
3031
from homeassistant.helpers.event import async_track_state_change_event
3132
from homeassistant.loader import async_get_loaded_integration
@@ -45,6 +46,7 @@
4546
DEFAULT_UPDATE_MODE,
4647
DOMAIN,
4748
LOGGER,
49+
REPAIR_ISSUE_TRIGGER_ENTITY_MISSING,
4850
SENSOR_TYPE_DATA,
4951
SENSOR_TYPE_VALUE,
5052
UPDATE_MODE_POLLING,
@@ -249,6 +251,30 @@ def async_service_removed_listener(event: Event) -> None:
249251
if update_mode == UPDATE_MODE_STATE_TRIGGER:
250252
trigger_entity = entry.data.get(CONF_TRIGGER_ENTITY, "")
251253
if trigger_entity:
254+
# Check if trigger entity exists
255+
if not hass.states.get(trigger_entity):
256+
# Create repair issue for missing trigger entity
257+
entry_name = entry.data.get("name", "Unknown")
258+
ir.async_create_issue(
259+
hass,
260+
DOMAIN,
261+
f"{REPAIR_ISSUE_TRIGGER_ENTITY_MISSING}_{entry.entry_id}",
262+
is_fixable=False,
263+
severity=ir.IssueSeverity.WARNING,
264+
translation_key="trigger_entity_missing",
265+
translation_placeholders={
266+
"entry_name": entry_name,
267+
"trigger_entity": trigger_entity,
268+
},
269+
)
270+
LOGGER.warning(
271+
"Trigger entity %s not found for config entry %s (%s). "
272+
"The integration will not update automatically until the entity becomes available.",
273+
trigger_entity,
274+
entry_name,
275+
entry.entry_id,
276+
)
277+
252278
trigger_from_state = entry.data.get(CONF_TRIGGER_FROM_STATE, "")
253279
trigger_to_state = entry.data.get(CONF_TRIGGER_TO_STATE, "")
254280

@@ -259,8 +285,43 @@ def async_state_change_listener(event: Event[EventStateChangedData]) -> None:
259285
new_state = event.data.get("new_state")
260286

261287
if new_state is None:
288+
# Entity was removed - create repair issue if it doesn't exist
289+
entry_name = entry.data.get("name", "Unknown")
290+
issue_id = f"{REPAIR_ISSUE_TRIGGER_ENTITY_MISSING}_{entry.entry_id}"
291+
292+
# Check if issue already exists
293+
existing_issue = ir.async_get(hass).async_get_issue(DOMAIN, issue_id)
294+
if not existing_issue:
295+
ir.async_create_issue(
296+
hass,
297+
DOMAIN,
298+
issue_id,
299+
is_fixable=False,
300+
severity=ir.IssueSeverity.WARNING,
301+
translation_key="trigger_entity_missing",
302+
translation_placeholders={
303+
"entry_name": entry_name,
304+
"trigger_entity": trigger_entity,
305+
},
306+
)
307+
LOGGER.warning(
308+
"Trigger entity %s was removed for config entry %s (%s)",
309+
trigger_entity,
310+
entry_name,
311+
entry.entry_id,
312+
)
262313
return
263314

315+
# Entity exists - delete repair issue if it exists
316+
issue_id = f"{REPAIR_ISSUE_TRIGGER_ENTITY_MISSING}_{entry.entry_id}"
317+
existing_issue = ir.async_get(hass).async_get_issue(DOMAIN, issue_id)
318+
if existing_issue:
319+
ir.async_delete_issue(hass, DOMAIN, issue_id)
320+
LOGGER.info(
321+
"Trigger entity %s is now available again, repair issue removed",
322+
trigger_entity,
323+
)
324+
264325
old_state_value = old_state.state if old_state else None
265326
new_state_value = new_state.state
266327

@@ -318,7 +379,7 @@ async def async_unload_entry(
318379
Unload a config entry.
319380
320381
This is called when the integration is being removed or reloaded.
321-
It ensures proper cleanup of all platform entities.
382+
It ensures proper cleanup of all platform entities and repair issues.
322383
323384
Args:
324385
hass: The Home Assistant instance.
@@ -327,6 +388,13 @@ async def async_unload_entry(
327388
Returns:
328389
True if unload was successful.
329390
"""
391+
# Clean up any repair issues for this entry
392+
issue_id = f"{REPAIR_ISSUE_TRIGGER_ENTITY_MISSING}_{entry.entry_id}"
393+
existing_issue = ir.async_get(hass).async_get_issue(DOMAIN, issue_id)
394+
if existing_issue:
395+
ir.async_delete_issue(hass, DOMAIN, issue_id)
396+
LOGGER.debug("Removed repair issue %s during unload", issue_id)
397+
330398
platforms = _get_platforms_for_entry(entry)
331399
return await hass.config_entries.async_unload_platforms(entry, platforms)
332400

custom_components/action_result/const.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@
9090
INITIAL_RETRY_DELAY_SECONDS = 30
9191
SERVICE_CALL_TIMEOUT_SECONDS = 30 # Timeout for service calls
9292
MAX_RETRY_DELAY_SECONDS = 300 # 5 minutes max backoff
93+
94+
# Repair issue IDs
95+
REPAIR_ISSUE_TRIGGER_ENTITY_MISSING = "trigger_entity_missing"

custom_components/action_result/coordinator/base.py

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
MAX_RETRY_DELAY_SECONDS,
3535
SERVICE_CALL_TIMEOUT_SECONDS,
3636
)
37-
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError, ServiceNotFound
37+
from homeassistant.exceptions import HomeAssistantError, ServiceNotFound
3838
from homeassistant.helpers import issue_registry as ir
3939
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
4040
from homeassistant.util import dt as dt_util
@@ -230,43 +230,12 @@ def _classify_error(self, exc: Exception) -> str:
230230
# Default to unknown (treat as potentially temporary)
231231
return ERROR_TYPE_UNKNOWN
232232

233-
def _is_auth_error(self, exc: Exception) -> bool:
234-
"""
235-
Check if an exception represents an authentication/authorization error.
236-
237-
Auth errors should trigger a reauth flow instead of just marking
238-
the entity as unavailable.
239-
240-
Args:
241-
exc: The exception to check.
242-
243-
Returns:
244-
True if this is an authentication error, False otherwise.
245-
"""
246-
error_str = str(exc).lower()
247-
248-
# Authentication/authorization error indicators
249-
auth_indicators = [
250-
"unauthorized",
251-
"forbidden",
252-
"authentication failed",
253-
"invalid api key",
254-
"invalid token",
255-
"invalid credentials",
256-
"permission denied",
257-
"access denied",
258-
"401",
259-
"403",
260-
]
261-
262-
return any(indicator in error_str for indicator in auth_indicators)
263-
264233
def _create_repair_issue(self, issue_type: str, error_msg: str) -> None:
265234
"""
266235
Create a repair issue for persistent problems.
267236
268237
Args:
269-
issue_type: Type of issue (service_not_found, auth_failed, etc.)
238+
issue_type: Type of issue (service_not_found, service_call_failed, etc.)
270239
error_msg: Error message to display to user.
271240
"""
272241
service_domain, service_name = self.get_service_info()
@@ -429,22 +398,6 @@ async def _async_update_data(self) -> dict[str, Any]:
429398
self.last_error_type = error_type
430399
self.consecutive_errors += 1
431400

432-
# Check if this is an authentication error
433-
if self._is_auth_error(exc):
434-
LOGGER.error(
435-
"Authentication error calling service %s for '%s': %s",
436-
service_full_name,
437-
entry_name,
438-
exc,
439-
)
440-
# Create repair issue for auth error
441-
self._create_repair_issue(
442-
"auth_failed",
443-
str(exc),
444-
)
445-
# Raise ConfigEntryAuthFailed to trigger reauth flow
446-
raise ConfigEntryAuthFailed(f"Authentication failed for {service_full_name}: {error_msg}") from exc
447-
448401
if error_type == ERROR_TYPE_TEMPORARY:
449402
self.is_retrying = True
450403
LOGGER.warning(
@@ -511,7 +464,6 @@ async def _async_update_data(self) -> dict[str, Any]:
511464

512465
# Delete repair issues if they exist
513466
self._delete_repair_issue("service_not_found")
514-
self._delete_repair_issue("auth_failed")
515467
self._delete_repair_issue("service_call_failed")
516468

517469
LOGGER.debug(

custom_components/action_result/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
"integration_type": "service",
1010
"iot_class": "local_polling",
1111
"issue_tracker": "https://github.com/jpawlowski/hass.action_result/issues",
12-
"version": "0.2.0"
12+
"version": "0.3.0"
1313
}

0 commit comments

Comments
 (0)