mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Add vacuum area mapping not configured issue (#163965)
This commit is contained in:
@@ -63,6 +63,7 @@ SERVICE_STOP = "stop"
|
||||
DEFAULT_NAME = "Vacuum cleaner robot"
|
||||
|
||||
ISSUE_SEGMENTS_CHANGED = "segments_changed"
|
||||
ISSUE_SEGMENTS_MAPPING_NOT_CONFIGURED = "segments_mapping_not_configured"
|
||||
|
||||
_BATTERY_DEPRECATION_IGNORED_PLATFORMS = ("template",)
|
||||
|
||||
@@ -189,6 +190,9 @@ class StateVacuumEntity(
|
||||
_attr_activity: VacuumActivity | None = None
|
||||
_attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0)
|
||||
|
||||
_segments_not_configured_issue_created: bool = False
|
||||
_segments_changed_last_seen: list[dict[str, Any]] | None = None
|
||||
|
||||
__vacuum_legacy_battery_level: bool = False
|
||||
__vacuum_legacy_battery_icon: bool = False
|
||||
__vacuum_legacy_battery_feature: bool = False
|
||||
@@ -232,6 +236,17 @@ class StateVacuumEntity(
|
||||
if self.__vacuum_legacy_battery_icon:
|
||||
self._report_deprecated_battery_properties("battery_icon")
|
||||
|
||||
@callback
|
||||
def async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine."""
|
||||
super().async_write_ha_state()
|
||||
self._async_check_segments_issues()
|
||||
|
||||
@callback
|
||||
def async_registry_entry_updated(self) -> None:
|
||||
"""Run when the entity registry entry has been updated."""
|
||||
self._async_check_segments_issues()
|
||||
|
||||
@callback
|
||||
def _report_deprecated_battery_properties(self, property: str) -> None:
|
||||
"""Report on deprecated use of battery properties.
|
||||
@@ -489,6 +504,61 @@ class StateVacuumEntity(
|
||||
"entity_id": self.entity_id,
|
||||
},
|
||||
)
|
||||
options: Mapping[str, Any] = self.registry_entry.options.get(DOMAIN, {})
|
||||
self._segments_changed_last_seen = options.get("last_seen_segments")
|
||||
|
||||
@callback
|
||||
def _async_check_segments_issues(self) -> None:
|
||||
"""Create or delete segment-related repair issues."""
|
||||
if self.registry_entry is None:
|
||||
return
|
||||
|
||||
options: Mapping[str, Any] = self.registry_entry.options.get(DOMAIN, {})
|
||||
should_have_not_configured_issue = (
|
||||
VacuumEntityFeature.CLEAN_AREA in self.supported_features
|
||||
and options.get("area_mapping") is None
|
||||
)
|
||||
|
||||
if (
|
||||
should_have_not_configured_issue
|
||||
and not self._segments_not_configured_issue_created
|
||||
):
|
||||
issue_id = (
|
||||
f"{ISSUE_SEGMENTS_MAPPING_NOT_CONFIGURED}_{self.registry_entry.id}"
|
||||
)
|
||||
ir.async_create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
issue_id,
|
||||
data={
|
||||
"entry_id": self.registry_entry.id,
|
||||
"entity_id": self.entity_id,
|
||||
},
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key=ISSUE_SEGMENTS_MAPPING_NOT_CONFIGURED,
|
||||
translation_placeholders={
|
||||
"entity_id": self.entity_id,
|
||||
},
|
||||
)
|
||||
self._segments_not_configured_issue_created = True
|
||||
elif (
|
||||
not should_have_not_configured_issue
|
||||
and self._segments_not_configured_issue_created
|
||||
):
|
||||
issue_id = (
|
||||
f"{ISSUE_SEGMENTS_MAPPING_NOT_CONFIGURED}_{self.registry_entry.id}"
|
||||
)
|
||||
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
|
||||
self._segments_not_configured_issue_created = False
|
||||
|
||||
if self._segments_changed_last_seen is not None and (
|
||||
VacuumEntityFeature.CLEAN_AREA not in self.supported_features
|
||||
or options.get("last_seen_segments") != self._segments_changed_last_seen
|
||||
):
|
||||
issue_id = f"{ISSUE_SEGMENTS_CHANGED}_{self.registry_entry.id}"
|
||||
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
|
||||
self._segments_changed_last_seen = None
|
||||
|
||||
def locate(self, **kwargs: Any) -> None:
|
||||
"""Locate the vacuum cleaner."""
|
||||
|
||||
@@ -93,6 +93,10 @@
|
||||
"segments_changed": {
|
||||
"description": "",
|
||||
"title": "Vacuum segments have changed for {entity_id}"
|
||||
},
|
||||
"segments_mapping_not_configured": {
|
||||
"description": "",
|
||||
"title": "Vacuum segment mapping not configured for {entity_id}"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
|
||||
@@ -430,10 +430,10 @@ async def test_last_seen_segments(
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("config_flow_fixture")
|
||||
async def test_last_seen_segments_and_issue_creation(
|
||||
async def test_segments_changed_issue(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test last_seen_segments property and segments issue creation."""
|
||||
"""Test segments changed issue."""
|
||||
mock_vacuum = MockVacuumWithCleanArea(name="Testing", entity_id="vacuum.testing")
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
@@ -452,6 +452,17 @@ async def test_last_seen_segments_and_issue_creation(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_entry = entity_registry.async_get(mock_vacuum.entity_id)
|
||||
|
||||
entity_registry.async_update_entity_options(
|
||||
mock_vacuum.entity_id,
|
||||
DOMAIN,
|
||||
{
|
||||
"area_mapping": {"area_1": ["seg_1"]},
|
||||
"last_seen_segments": [asdict(segment) for segment in mock_vacuum.segments],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vacuum.async_create_segments_issue()
|
||||
|
||||
issue_id = f"segments_changed_{entity_entry.id}"
|
||||
@@ -460,6 +471,95 @@ async def test_last_seen_segments_and_issue_creation(
|
||||
assert issue.severity == ir.IssueSeverity.WARNING
|
||||
assert issue.translation_key == "segments_changed"
|
||||
|
||||
entity_registry.async_update_entity_options(
|
||||
mock_vacuum.entity_id,
|
||||
DOMAIN,
|
||||
{
|
||||
"area_mapping": {"area_1": ["seg_1"], "area_2": ["seg_new"]},
|
||||
"last_seen_segments": [
|
||||
{"id": "seg_1", "name": "Kitchen"},
|
||||
{"id": "seg_new", "name": "New Room"},
|
||||
],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert ir.async_get(hass).async_get_issue(DOMAIN, issue_id) is None
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("config_flow_fixture")
|
||||
@pytest.mark.parametrize("area_mapping", [{"area_1": ["seg_1"]}, {}])
|
||||
async def test_segments_mapping_not_configured_issue(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
area_mapping: dict[str, list[str]],
|
||||
) -> None:
|
||||
"""Test segments_mapping_not_configured issue."""
|
||||
mock_vacuum = MockVacuumWithCleanArea(name="Testing", entity_id="vacuum.testing")
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test",
|
||||
async_setup_entry=help_async_setup_entry_init,
|
||||
async_unload_entry=help_async_unload_entry,
|
||||
),
|
||||
)
|
||||
setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_entry = entity_registry.async_get(mock_vacuum.entity_id)
|
||||
|
||||
issue_id = f"segments_mapping_not_configured_{entity_entry.id}"
|
||||
issue = ir.async_get(hass).async_get_issue(DOMAIN, issue_id)
|
||||
assert issue is not None
|
||||
assert issue.severity == ir.IssueSeverity.WARNING
|
||||
assert issue.translation_key == "segments_mapping_not_configured"
|
||||
|
||||
entity_registry.async_update_entity_options(
|
||||
mock_vacuum.entity_id,
|
||||
DOMAIN,
|
||||
{
|
||||
"area_mapping": area_mapping,
|
||||
"last_seen_segments": [asdict(segment) for segment in mock_vacuum.segments],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert ir.async_get(hass).async_get_issue(DOMAIN, issue_id) is None
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("config_flow_fixture")
|
||||
async def test_no_segments_mapping_issue_without_clean_area(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test no repair issue is created when CLEAN_AREA is not supported."""
|
||||
mock_vacuum = MockVacuum(name="Testing", entity_id="vacuum.testing")
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test",
|
||||
async_setup_entry=help_async_setup_entry_init,
|
||||
async_unload_entry=help_async_unload_entry,
|
||||
),
|
||||
)
|
||||
setup_test_component_platform(hass, DOMAIN, [mock_vacuum], from_config_entry=True)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
issues = ir.async_get(hass).issues
|
||||
assert not any(
|
||||
issue_id[1].startswith("segments_mapping_not_configured") for issue_id in issues
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("is_built_in", "log_warnings"), [(True, 0), (False, 3)])
|
||||
async def test_vacuum_log_deprecated_battery_using_properties(
|
||||
|
||||
Reference in New Issue
Block a user