121 lines
4.6 KiB
Python
121 lines
4.6 KiB
Python
"""Text entity for Watchman ignored labels."""
|
|
from homeassistant.components.text import TextEntity
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import issue_registry as ir, label_registry as lr
|
|
from homeassistant.helpers.entity import EntityCategory
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.restore_state import RestoreEntity
|
|
|
|
from .const import CONF_IGNORED_LABELS, DOMAIN
|
|
from .coordinator import WatchmanCoordinator
|
|
|
|
|
|
class WatchmanIgnoredLabelsText(RestoreEntity, TextEntity):
|
|
"""Text entity to manage ignored labels."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_translation_key = "ignored_labels"
|
|
_attr_icon = "mdi:label-off"
|
|
|
|
def __init__(self, hass: HomeAssistant, coordinator: WatchmanCoordinator) -> None:
|
|
"""Initialize the entity."""
|
|
self.hass = hass
|
|
self.coordinator = coordinator
|
|
self._attr_unique_id = f"{DOMAIN}_ignored_labels"
|
|
self.entity_id = f"text.{DOMAIN}_ignored_labels"
|
|
self._attr_native_value = ""
|
|
# Orphan entity: No device_info, so it won't appear on the device page
|
|
|
|
@property
|
|
def native_value(self) -> str:
|
|
"""Return the value of the text entity."""
|
|
labels = self.coordinator.config_entry.data.get(CONF_IGNORED_LABELS, [])
|
|
return ", ".join(labels)
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Handle entity which will be added."""
|
|
await super().async_added_to_hass()
|
|
|
|
# Migration: Restore state to config entry if key is missing
|
|
if CONF_IGNORED_LABELS not in self.coordinator.config_entry.data:
|
|
if (state := await self.async_get_last_state()) is not None:
|
|
restored_labels = self._parse_labels(state.state)
|
|
if restored_labels:
|
|
self.hass.config_entries.async_update_entry(
|
|
self.coordinator.config_entry,
|
|
data={**self.coordinator.config_entry.data, CONF_IGNORED_LABELS: restored_labels}
|
|
)
|
|
else:
|
|
# If no restored state, initialize key to empty list to mark migration done
|
|
self.hass.config_entries.async_update_entry(
|
|
self.coordinator.config_entry,
|
|
data={**self.coordinator.config_entry.data, CONF_IGNORED_LABELS: []}
|
|
)
|
|
|
|
async def async_set_value(self, value: str) -> None:
|
|
"""Set the text value."""
|
|
# Create deprecation issue
|
|
ir.async_create_issue(
|
|
self.hass,
|
|
DOMAIN,
|
|
"deprecated_ignored_labels_entity",
|
|
is_fixable=False,
|
|
severity=ir.IssueSeverity.WARNING,
|
|
translation_key="deprecated_text_entity",
|
|
translation_placeholders={
|
|
"entity_id": self.entity_id,
|
|
},
|
|
)
|
|
|
|
valid_labels, invalid_labels = self._validate_labels(value)
|
|
|
|
if invalid_labels:
|
|
await self.hass.services.async_call(
|
|
"persistent_notification",
|
|
"create",
|
|
{
|
|
"title": "Watchman: Invalid Labels",
|
|
"message": f"The following labels were not found and ignored: {', '.join(invalid_labels)}",
|
|
"notification_id": "watchman_invalid_labels",
|
|
},
|
|
)
|
|
|
|
# Save to config entry (triggers reload)
|
|
self.hass.config_entries.async_update_entry(
|
|
self.coordinator.config_entry,
|
|
data={**self.coordinator.config_entry.data, CONF_IGNORED_LABELS: valid_labels}
|
|
)
|
|
|
|
def _parse_labels(self, value: str) -> list[str]:
|
|
"""Parse comma-separated string to list."""
|
|
if not value:
|
|
return []
|
|
return [x.strip() for x in value.split(",") if x.strip()]
|
|
|
|
def _validate_labels(self, value: str) -> tuple[list[str], list[str]]:
|
|
"""Validate labels against registry."""
|
|
registry = lr.async_get(self.hass)
|
|
existing_labels = {l.label_id for l in registry.async_list_labels()}
|
|
|
|
input_labels = self._parse_labels(value)
|
|
valid = []
|
|
invalid = []
|
|
|
|
for label in input_labels:
|
|
if label in existing_labels:
|
|
valid.append(label)
|
|
else:
|
|
invalid.append(label)
|
|
|
|
return valid, invalid
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the text platform."""
|
|
coordinator = config_entry.runtime_data.coordinator
|
|
async_add_entities([WatchmanIgnoredLabelsText(hass, coordinator)])
|