diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py new file mode 100644 index 00000000..5c79d593 --- /dev/null +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py @@ -0,0 +1,64 @@ +"""Spook - Your homie.""" + +from __future__ import annotations + +from homeassistant.components import automation +from homeassistant.helpers import label_registry as lr +from homeassistant.helpers.entity_component import DATA_INSTANCES, EntityComponent + +from ....const import LOGGER +from ....repairs import AbstractSpookRepair +from ....util import async_filter_known_label_ids, async_get_all_label_ids + + +class SpookRepair(AbstractSpookRepair): + """Spook repair tries to find unknown referenced labels in automations.""" + + domain = automation.DOMAIN + repair = "automation_unknown_label_references" + inspect_events = { + lr.EVENT_LABEL_REGISTRY_UPDATED, + } + inspect_on_reload = True + + automatically_clean_up_issues = True + + async def async_inspect(self) -> None: + """Trigger a inspection.""" + if self.domain not in self.hass.data[DATA_INSTANCES]: + return + + entity_component: EntityComponent[automation.AutomationEntity] = self.hass.data[ + DATA_INSTANCES + ][self.domain] + + LOGGER.debug("Spook is inspecting: %s", self.repair) + + known_label_ids = async_get_all_label_ids(self.hass) + + for entity in entity_component.entities: + self.possible_issue_ids.add(entity.entity_id) + if not isinstance(entity, automation.UnavailableAutomationEntity) and ( + unknown_labels := async_filter_known_label_ids( + self.hass, + label_ids=entity.referenced_labels, + known_label_ids=known_label_ids, + ) + ): + self.async_create_issue( + issue_id=entity.entity_id, + translation_placeholders={ + "labels": "\n".join(f"- `{label}`" for label in unknown_labels), + "automation": entity.name, + "edit": f"/config/automation/edit/{entity.unique_id}", + "entity_id": entity.entity_id, + }, + ) + LOGGER.debug( + ( + "Spook found unknown labels in %s " + "and created an issue for it; Labels: %s", + ), + entity.entity_id, + ", ".join(unknown_labels), + ) diff --git a/custom_components/spook/translations/en.json b/custom_components/spook/translations/en.json index b1fee5e4..77089db4 100644 --- a/custom_components/spook/translations/en.json +++ b/custom_components/spook/translations/en.json @@ -233,6 +233,10 @@ "description": "Spook has found a ghost in your automations 👻\n\nWhile floating around, Spook crossed path with the following automation:\n\n[{automation}]({edit}) (`{entity_id}`)\n\nThis automation references the following entities, which are unknown to Home Assistant:\n\n{entities}\n\n\n\nTo fix this error, [edit the automation]({edit}) and remove the use of these non-existing entities.\n\nSpook 👻 Your homie.", "title": "Unknown entities used in: {automation}" }, + "automation_unknown_label_references": { + "description": "Spook has found a ghost in your automations 👻\n\nWhile floating around, Spook crossed path with the following automation:\n\n[{automation}]({edit}) (`{entity_id}`)\n\nThis automation references the following labels, which are unknown to Home Assistant:\n\n{labels}\n\n\n\nTo fix this error, [edit the automation]({edit}) and remove the use of these non-existing labels.\n\nSpook 👻 Your homie.", + "title": "Unknown label used in: {automation}" + }, "automation_unknown_service_references": { "description": "Spook has found a ghost in your automations 👻\n\nWhile floating around, Spook crossed path with the following automation:\n\n[{automation}]({edit}) (`{entity_id}`)\n\nThis automation references the following services, which are unknown to Home Assistant:\n\n{services}\n\n\n\nTo fix this error, [edit the automation]({edit}) and remove the use of these non-existing services.\n\nSpook 👻 Your homie.", "title": "Unknown services used in: {automation}" diff --git a/custom_components/spook/util.py b/custom_components/spook/util.py index df5a7846..c2f05bbd 100644 --- a/custom_components/spook/util.py +++ b/custom_components/spook/util.py @@ -25,6 +25,7 @@ config_validation as cv, device_registry as dr, entity_registry as er, + label_registry as lr, ) from homeassistant.helpers.template import Template @@ -224,6 +225,30 @@ def async_filter_known_entity_ids( } +@callback +def async_get_all_label_ids(hass: HomeAssistant) -> set[str]: + """Return all label IDs, known to Home Assistant.""" + label_registry = lr.async_get(hass) + return {label.label_id for label in label_registry.labels.values()} + + +@callback +def async_filter_known_label_ids( + hass: HomeAssistant, + *, + label_ids: set[str], + known_label_ids: set[str] | None = None, +) -> set[str]: + """Filter out known label IDs.""" + if known_label_ids is None: + known_label_ids = async_get_all_label_ids(hass) + return { + label_id + for label_id in label_ids - known_label_ids + if label_id and isinstance(label_id, str) + } + + @callback def async_get_all_services(hass: HomeAssistant) -> set[str]: """Return all services, known to Home Assistant."""