Skip to content

Commit f804134

Browse files
WIP on repair
1 parent 229cf4a commit f804134

File tree

4 files changed

+213
-10
lines changed

4 files changed

+213
-10
lines changed

custom_components/calendar_event/__init__.py

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@
1414
from homeassistant.helpers import config_validation as cv
1515
from homeassistant.helpers import entity_registry as er
1616
from homeassistant.helpers.helper_integration import async_handle_source_entity_changes
17+
from homeassistant.helpers.issue_registry import (
18+
IssueSeverity,
19+
async_create_issue,
20+
async_delete_issue,
21+
)
1722
from homeassistant.helpers.typing import ConfigType
1823

1924
from .const import (
2025
CONF_CALENDAR_ENTITY_ID,
2126
DOMAIN,
27+
ISSUE_MISSING_CALENDAR_ENTITY,
2228
LOGGER,
2329
MIN_HA_VERSION,
2430
PLATFORMS,
@@ -49,31 +55,69 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
4955
"""Set up calendar_event from a config entry."""
5056

5157
entity_registry = er.async_get(hass)
52-
try:
53-
entity_id = er.async_validate_entity_id( # noqa: F841
54-
entity_registry, entry.options[CONF_CALENDAR_ENTITY_ID]
58+
59+
# try:
60+
# entity_id = er.async_validate_entity_id( # noqa: F841
61+
# entity_registry, entry.options[CONF_CALENDAR_ENTITY_ID]
62+
63+
if (
64+
calendar_entity := entity_registry.entities.get_entry(
65+
entry.options[CONF_CALENDAR_ENTITY_ID]
5566
)
56-
except vol.Invalid:
57-
# The entity is identified by an unknown entity registry ID
58-
LOGGER.error(
59-
"Failed to setup calender_event for unknown entity %s",
60-
entry.options[CONF_CALENDAR_ENTITY_ID],
67+
) is None:
68+
async_create_issue(
69+
hass,
70+
DOMAIN,
71+
f"{ISSUE_MISSING_CALENDAR_ENTITY}_{entry.options[CONF_CALENDAR_ENTITY_ID]}",
72+
is_fixable=True,
73+
severity=IssueSeverity.ERROR,
74+
translation_key="missing_calendar_entity",
75+
translation_placeholders={
76+
"calendar_entity": entry.options[CONF_CALENDAR_ENTITY_ID],
77+
"entry_title": entry.title or "Calendar Event",
78+
},
79+
data={"entry_id": entry.entry_id},
6180
)
81+
6282
return False
6383

84+
# Clean up any existing repair issues since the entity is now valid
85+
async_delete_issue(
86+
hass, DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{entry.entry_id}"
87+
)
88+
6489
def set_source_entity_id_or_uuid(source_entity_id: str) -> None:
6590
hass.config_entries.async_update_entry(
6691
entry,
6792
options={**entry.options, CONF_CALENDAR_ENTITY_ID: source_entity_id},
6893
)
94+
# Clean up repair issue when entity is updated
95+
async_delete_issue(
96+
hass, DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{entry.entry_id}"
97+
)
6998

7099
async def source_entity_removed() -> None:
71100
# The source entity has been removed.
72101
LOGGER.error(
73-
"Failed to setup calender_event for unknown entity %s",
102+
"Failed to setup calendar_event for unknown entity %s",
74103
entry.options[CONF_CALENDAR_ENTITY_ID],
75104
)
76105

106+
# Create a repair issue for the removed calendar entity
107+
async_create_issue(
108+
hass,
109+
DOMAIN,
110+
f"{ISSUE_MISSING_CALENDAR_ENTITY}_{entry.entry_id}",
111+
is_fixable=True,
112+
severity=IssueSeverity.ERROR,
113+
translation_key="missing_calendar_entity",
114+
translation_placeholders={
115+
"calendar_entity": entry.options[CONF_CALENDAR_ENTITY_ID],
116+
"entry_title": entry.title or "Calendar Event",
117+
},
118+
data={"entry_id": entry.entry_id},
119+
)
120+
77121
entry.async_on_unload(
78122
async_handle_source_entity_changes(
79123
hass,
@@ -99,4 +143,10 @@ async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry)
99143

100144
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
101145
"""Unload a config entry."""
146+
147+
# Clean up any repair issues when unloading the entry
148+
async_delete_issue(
149+
hass, DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{entry.entry_id}"
150+
)
151+
102152
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Repairs for calendar_event integration."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, cast
6+
7+
import voluptuous as vol
8+
from homeassistant import data_entry_flow
9+
from homeassistant.components.repairs import RepairsFlow
10+
from homeassistant.core import HomeAssistant
11+
from homeassistant.helpers import selector
12+
from homeassistant.helpers.issue_registry import async_delete_issue
13+
14+
from .const import CONF_CALENDAR_ENTITY_ID, DOMAIN, ISSUE_MISSING_CALENDAR_ENTITY
15+
16+
if TYPE_CHECKING:
17+
from homeassistant.config_entries import ConfigEntry
18+
19+
20+
class CalendarEntityMissingRepairFlow(RepairsFlow):
21+
"""Handler for calendar entity missing repair flow."""
22+
23+
def __init__(self, entry: ConfigEntry) -> None:
24+
"""Initialize the repair flow."""
25+
super().__init__()
26+
self.entry = entry
27+
28+
async def async_step_confirm(
29+
self, user_input: dict[str, str] | None = None
30+
) -> data_entry_flow.FlowResult:
31+
"""Handle the confirm step of the repair flow."""
32+
if user_input is not None:
33+
# Update the config entry with the new calendar entity
34+
new_options = {**self.entry.options}
35+
new_options[CONF_CALENDAR_ENTITY_ID] = user_input[CONF_CALENDAR_ENTITY_ID]
36+
37+
self.hass.config_entries.async_update_entry(
38+
self.entry, options=new_options
39+
)
40+
41+
# Clean up the repair issue since it's now fixed
42+
async_delete_issue(
43+
self.hass, DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{self.entry.entry_id}"
44+
)
45+
46+
return self.async_create_entry(data={})
47+
48+
return self.async_show_form(
49+
step_id="confirm",
50+
data_schema=vol.Schema({
51+
vol.Required(CONF_CALENDAR_ENTITY_ID): selector.EntitySelector(
52+
selector.EntitySelectorConfig(domain="calendar")
53+
),
54+
}),
55+
description_placeholders={
56+
"calendar_entity": self.entry.options[CONF_CALENDAR_ENTITY_ID],
57+
"entry_title": self.entry.title or "Calendar Event",
58+
},
59+
)
60+
61+
62+
async def async_create_fix_flow(
63+
hass: HomeAssistant,
64+
issue_id: str,
65+
data: dict[str, str | int | float | None] | None,
66+
) -> RepairsFlow:
67+
"""Create flow."""
68+
if data and (entry_id := data.get("entry_id")):
69+
entry_id = cast(str, entry_id)
70+
entry = hass.config_entries.async_get_entry(entry_id)
71+
if entry:
72+
return CalendarEntityMissingRepairFlow(entry)
73+
74+
# Fallback to a basic confirm flow if entry not found
75+
from homeassistant.components.repairs.issue_handler import ConfirmRepairFlow
76+
77+
return ConfirmRepairFlow()

custom_components/calendar_event/translations/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"issues": {
3636
"missing_calendar_entity": {
3737
"title": "Calendar entity missing for {entry_title}",
38-
"description": "The calendar entity `{calendar_entity}` is no longer available. Please update the configuration to select a different calendar entity.",
38+
"description": "The calendar entity `{calendar_entity}` is no longer available. Please select a new calendar entity to continue using this Calendar Event helper.",
3939
"fix_flow": {
4040
"step": {
4141
"confirm": {

tests/test_repairs.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Test repairs for calendar_event integration."""
2+
3+
from __future__ import annotations
4+
5+
from homeassistant.core import HomeAssistant
6+
from homeassistant.helpers import issue_registry as ir
7+
from pytest_homeassistant_custom_component.common import MockConfigEntry
8+
9+
from custom_components.calendar_event.const import DOMAIN, ISSUE_MISSING_CALENDAR_ENTITY
10+
11+
12+
async def test_repair_issue_created_for_missing_calendar_entity(
13+
hass: HomeAssistant,
14+
) -> None:
15+
"""Test that a repair issue is created when calendar entity is missing."""
16+
17+
# Create a config entry with a non-existent calendar entity
18+
config_entry = MockConfigEntry(
19+
domain=DOMAIN,
20+
options={
21+
"calendar_entity_id": "calendar.nonexistent",
22+
"summary": "test"
23+
},
24+
title="Test Calendar Event",
25+
)
26+
config_entry.add_to_hass(hass)
27+
28+
# Setup should fail and create a repair issue
29+
assert not await hass.config_entries.async_setup(config_entry.entry_id)
30+
31+
# Check that a repair issue was created
32+
issue_registry = ir.async_get(hass)
33+
issue = issue_registry.async_get_issue(
34+
DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{config_entry.entry_id}"
35+
)
36+
37+
assert issue is not None
38+
assert issue.translation_key == "missing_calendar_entity"
39+
assert issue.is_fixable is True
40+
assert issue.severity == ir.IssueSeverity.ERROR
41+
42+
43+
async def test_repair_issue_cleaned_up_when_entry_unloaded(
44+
hass: HomeAssistant,
45+
) -> None:
46+
"""Test that repair issues are cleaned up when config entry is unloaded."""
47+
48+
# Create a config entry with a non-existent calendar entity
49+
config_entry = MockConfigEntry(
50+
domain=DOMAIN,
51+
options={
52+
"calendar_entity_id": "calendar.nonexistent",
53+
"summary": "test"
54+
},
55+
title="Test Calendar Event",
56+
)
57+
config_entry.add_to_hass(hass)
58+
59+
# Setup should fail and create a repair issue
60+
assert not await hass.config_entries.async_setup(config_entry.entry_id)
61+
62+
# Verify repair issue exists
63+
issue_registry = ir.async_get(hass)
64+
issue = issue_registry.async_get_issue(
65+
DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{config_entry.entry_id}"
66+
)
67+
assert issue is not None
68+
69+
# Remove the config entry
70+
await hass.config_entries.async_remove(config_entry.entry_id)
71+
72+
# Verify repair issue is cleaned up
73+
issue = issue_registry.async_get_issue(
74+
DOMAIN, f"{ISSUE_MISSING_CALENDAR_ENTITY}_{config_entry.entry_id}"
75+
)
76+
assert issue is None

0 commit comments

Comments
 (0)