diff --git a/homeassistant/components/habitica/button.py b/homeassistant/components/habitica/button.py index 626655da1aa..cdd166a4444 100644 --- a/homeassistant/components/habitica/button.py +++ b/homeassistant/components/habitica/button.py @@ -6,21 +6,19 @@ from collections.abc import Callable from dataclasses import dataclass from enum import StrEnum from http import HTTPStatus -from typing import TYPE_CHECKING, Any +from typing import Any from aiohttp import ClientResponseError from homeassistant.components.button import ButtonEntity, ButtonEntityDescription -from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import HabiticaConfigEntry -from .const import DOMAIN, MANUFACTURER, NAME +from .const import DOMAIN from .coordinator import HabiticaData, HabiticaDataUpdateCoordinator +from .entity import HabiticaBase @dataclass(kw_only=True, frozen=True) @@ -90,34 +88,11 @@ async def async_setup_entry( ) -class HabiticaButton(CoordinatorEntity[HabiticaDataUpdateCoordinator], ButtonEntity): +class HabiticaButton(HabiticaBase, ButtonEntity): """Representation of a Habitica button.""" - _attr_has_entity_name = True entity_description: HabiticaButtonEntityDescription - def __init__( - self, - coordinator: HabiticaDataUpdateCoordinator, - entity_description: HabiticaButtonEntityDescription, - ) -> None: - """Initialize a Habitica button.""" - super().__init__(coordinator) - if TYPE_CHECKING: - assert coordinator.config_entry.unique_id - self.entity_description = entity_description - self._attr_unique_id = ( - f"{coordinator.config_entry.unique_id}_{entity_description.key}" - ) - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - manufacturer=MANUFACTURER, - model=NAME, - name=coordinator.config_entry.data[CONF_NAME], - configuration_url=coordinator.config_entry.data[CONF_URL], - identifiers={(DOMAIN, coordinator.config_entry.unique_id)}, - ) - async def async_press(self) -> None: """Handle the button press.""" try: diff --git a/homeassistant/components/habitica/const.py b/homeassistant/components/habitica/const.py index 43f8906ca64..5a567abe5de 100644 --- a/homeassistant/components/habitica/const.py +++ b/homeassistant/components/habitica/const.py @@ -21,3 +21,5 @@ MANUFACTURER = "HabitRPG, Inc." NAME = "Habitica" ADDITIONAL_USER_FIELDS: set[str] = {"lastCron"} + +UNIT_TASKS = "tasks" diff --git a/homeassistant/components/habitica/entity.py b/homeassistant/components/habitica/entity.py new file mode 100644 index 00000000000..7f29a698bad --- /dev/null +++ b/homeassistant/components/habitica/entity.py @@ -0,0 +1,42 @@ +"""Base entity for Habitica.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from homeassistant.const import CONF_NAME, CONF_URL +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.entity import EntityDescription +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, MANUFACTURER, NAME +from .coordinator import HabiticaDataUpdateCoordinator + + +class HabiticaBase(CoordinatorEntity[HabiticaDataUpdateCoordinator]): + """Base Habitica entity.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: HabiticaDataUpdateCoordinator, + entity_description: EntityDescription, + context: Any = None, + ) -> None: + """Initialize a Habitica entity.""" + super().__init__(coordinator, context) + if TYPE_CHECKING: + assert coordinator.config_entry.unique_id + self.entity_description = entity_description + self._attr_unique_id = ( + f"{coordinator.config_entry.unique_id}_{entity_description.key}" + ) + self._attr_device_info = DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + manufacturer=MANUFACTURER, + model=NAME, + name=coordinator.config_entry.data[CONF_NAME], + configuration_url=coordinator.config_entry.data[CONF_URL], + identifiers={(DOMAIN, coordinator.config_entry.unique_id)}, + ) diff --git a/homeassistant/components/habitica/icons.json b/homeassistant/components/habitica/icons.json index eed8ad5b9b5..710b8c9d25b 100644 --- a/homeassistant/components/habitica/icons.json +++ b/homeassistant/components/habitica/icons.json @@ -64,6 +64,18 @@ "wizard": "mdi:wizard-hat", "rogue": "mdi:ninja" } + }, + "todos": { + "default": "mdi:checkbox-outline" + }, + "dailys": { + "default": "mdi:calendar-month" + }, + "habits": { + "default": "mdi:contrast-box" + }, + "rewards": { + "default": "mdi:treasure-chest" } }, "switch": { diff --git a/homeassistant/components/habitica/sensor.py b/homeassistant/components/habitica/sensor.py index 31ecbcfd41c..b0514d27c11 100644 --- a/homeassistant/components/habitica/sensor.py +++ b/homeassistant/components/habitica/sensor.py @@ -2,11 +2,11 @@ from __future__ import annotations -from collections import namedtuple +from collections.abc import Callable, Mapping from dataclasses import dataclass from enum import StrEnum import logging -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Any from homeassistant.components.sensor import ( DOMAIN as SENSOR_DOMAIN, @@ -14,11 +14,8 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.issue_registry import ( IssueSeverity, @@ -26,11 +23,11 @@ from homeassistant.helpers.issue_registry import ( async_delete_issue, ) from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import HabiticaConfigEntry -from .const import DOMAIN, MANUFACTURER, NAME +from .const import DOMAIN, UNIT_TASKS from .coordinator import HabiticaDataUpdateCoordinator +from .entity import HabiticaBase from .util import entity_used_in _LOGGER = logging.getLogger(__name__) @@ -40,7 +37,15 @@ _LOGGER = logging.getLogger(__name__) class HabitipySensorEntityDescription(SensorEntityDescription): """Habitipy Sensor Description.""" - value_path: list[str] + value_fn: Callable[[dict[str, Any]], StateType] + ctx: str + + +@dataclass(kw_only=True, frozen=True) +class HabitipyTaskSensorEntityDescription(SensorEntityDescription): + """Habitipy Task Sensor Description.""" + + value_fn: Callable[[list[dict[str, Any]]], list[dict[str, Any]]] class HabitipySensorEntity(StrEnum): @@ -56,87 +61,88 @@ class HabitipySensorEntity(StrEnum): LEVEL = "level" GOLD = "gold" CLASS = "class" + HABITS = "habits" + DAILIES = "dailys" + TODOS = "todos" + REWARDS = "rewards" -SENSOR_DESCRIPTIONS: dict[str, HabitipySensorEntityDescription] = { - HabitipySensorEntity.DISPLAY_NAME: HabitipySensorEntityDescription( +SENSOR_DESCRIPTIONS: tuple[HabitipySensorEntityDescription, ...] = ( + HabitipySensorEntityDescription( key=HabitipySensorEntity.DISPLAY_NAME, translation_key=HabitipySensorEntity.DISPLAY_NAME, - value_path=["profile", "name"], + value_fn=lambda user: user.get("profile", {}).get("name"), + ctx="profile", ), - HabitipySensorEntity.HEALTH: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.HEALTH, translation_key=HabitipySensorEntity.HEALTH, native_unit_of_measurement="HP", suggested_display_precision=0, - value_path=["stats", "hp"], + value_fn=lambda user: user.get("stats", {}).get("hp"), + ctx="stats", ), - HabitipySensorEntity.HEALTH_MAX: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.HEALTH_MAX, translation_key=HabitipySensorEntity.HEALTH_MAX, native_unit_of_measurement="HP", entity_registry_enabled_default=False, - value_path=["stats", "maxHealth"], + value_fn=lambda user: user.get("stats", {}).get("maxHealth"), + ctx="stats", ), - HabitipySensorEntity.MANA: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.MANA, translation_key=HabitipySensorEntity.MANA, native_unit_of_measurement="MP", suggested_display_precision=0, - value_path=["stats", "mp"], + value_fn=lambda user: user.get("stats", {}).get("mp"), + ctx="stats", ), - HabitipySensorEntity.MANA_MAX: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.MANA_MAX, translation_key=HabitipySensorEntity.MANA_MAX, native_unit_of_measurement="MP", - value_path=["stats", "maxMP"], + value_fn=lambda user: user.get("stats", {}).get("maxMP"), + ctx="stats", ), - HabitipySensorEntity.EXPERIENCE: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.EXPERIENCE, translation_key=HabitipySensorEntity.EXPERIENCE, native_unit_of_measurement="XP", - value_path=["stats", "exp"], + value_fn=lambda user: user.get("stats", {}).get("exp"), + ctx="stats", ), - HabitipySensorEntity.EXPERIENCE_MAX: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.EXPERIENCE_MAX, translation_key=HabitipySensorEntity.EXPERIENCE_MAX, native_unit_of_measurement="XP", - value_path=["stats", "toNextLevel"], + value_fn=lambda user: user.get("stats", {}).get("toNextLevel"), + ctx="stats", ), - HabitipySensorEntity.LEVEL: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.LEVEL, translation_key=HabitipySensorEntity.LEVEL, - value_path=["stats", "lvl"], + value_fn=lambda user: user.get("stats", {}).get("lvl"), + ctx="stats", ), - HabitipySensorEntity.GOLD: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.GOLD, translation_key=HabitipySensorEntity.GOLD, native_unit_of_measurement="GP", suggested_display_precision=2, - value_path=["stats", "gp"], + value_fn=lambda user: user.get("stats", {}).get("gp"), + ctx="stats", ), - HabitipySensorEntity.CLASS: HabitipySensorEntityDescription( + HabitipySensorEntityDescription( key=HabitipySensorEntity.CLASS, translation_key=HabitipySensorEntity.CLASS, - value_path=["stats", "class"], + value_fn=lambda user: user.get("stats", {}).get("class"), device_class=SensorDeviceClass.ENUM, options=["warrior", "healer", "wizard", "rogue"], + ctx="stats", ), -} +) -SensorType = namedtuple("SensorType", ["name", "icon", "unit", "path"]) -TASKS_TYPES = { - "habits": SensorType( - "Habits", "mdi:clipboard-list-outline", "n_of_tasks", ["habit"] - ), - "dailys": SensorType( - "Dailys", "mdi:clipboard-list-outline", "n_of_tasks", ["daily"] - ), - "todos": SensorType("TODOs", "mdi:clipboard-list-outline", "n_of_tasks", ["todo"]), - "rewards": SensorType( - "Rewards", "mdi:clipboard-list-outline", "n_of_tasks", ["reward"] - ), -} TASKS_MAP_ID = "id" TASKS_MAP = { @@ -166,6 +172,36 @@ TASKS_MAP = { } +TASK_SENSOR_DESCRIPTION: tuple[HabitipyTaskSensorEntityDescription, ...] = ( + HabitipyTaskSensorEntityDescription( + key=HabitipySensorEntity.HABITS, + translation_key=HabitipySensorEntity.HABITS, + native_unit_of_measurement=UNIT_TASKS, + value_fn=lambda tasks: [r for r in tasks if r.get("type") == "habit"], + ), + HabitipyTaskSensorEntityDescription( + key=HabitipySensorEntity.DAILIES, + translation_key=HabitipySensorEntity.DAILIES, + native_unit_of_measurement=UNIT_TASKS, + value_fn=lambda tasks: [r for r in tasks if r.get("type") == "daily"], + ), + HabitipyTaskSensorEntityDescription( + key=HabitipySensorEntity.TODOS, + translation_key=HabitipySensorEntity.TODOS, + native_unit_of_measurement=UNIT_TASKS, + value_fn=lambda tasks: [ + r for r in tasks if r.get("type") == "todo" and not r.get("completed") + ], + ), + HabitipyTaskSensorEntityDescription( + key=HabitipySensorEntity.REWARDS, + translation_key=HabitipySensorEntity.REWARDS, + native_unit_of_measurement=UNIT_TASKS, + value_fn=lambda tasks: [r for r in tasks if r.get("type") == "reward"], + ), +) + + async def async_setup_entry( hass: HomeAssistant, config_entry: HabiticaConfigEntry, @@ -173,121 +209,70 @@ async def async_setup_entry( ) -> None: """Set up the habitica sensors.""" - name = config_entry.data[CONF_NAME] coordinator = config_entry.runtime_data entities: list[SensorEntity] = [ - HabitipySensor(coordinator, description, config_entry) - for description in SENSOR_DESCRIPTIONS.values() + HabitipySensor(coordinator, description) for description in SENSOR_DESCRIPTIONS ] entities.extend( - HabitipyTaskSensor(name, task_type, coordinator, config_entry) - for task_type in TASKS_TYPES + HabitipyTaskSensor(coordinator, description) + for description in TASK_SENSOR_DESCRIPTION ) async_add_entities(entities, True) -class HabitipySensor(CoordinatorEntity[HabiticaDataUpdateCoordinator], SensorEntity): +class HabitipySensor(HabiticaBase, SensorEntity): """A generic Habitica sensor.""" - _attr_has_entity_name = True entity_description: HabitipySensorEntityDescription def __init__( self, coordinator: HabiticaDataUpdateCoordinator, entity_description: HabitipySensorEntityDescription, - entry: ConfigEntry, ) -> None: """Initialize a generic Habitica sensor.""" - super().__init__(coordinator, context=entity_description.value_path[0]) - if TYPE_CHECKING: - assert entry.unique_id - self.entity_description = entity_description - self._attr_unique_id = f"{entry.unique_id}_{entity_description.key}" - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - manufacturer=MANUFACTURER, - model=NAME, - name=entry.data[CONF_NAME], - configuration_url=entry.data[CONF_URL], - identifiers={(DOMAIN, entry.unique_id)}, + super().__init__( + coordinator, + entity_description, + context=entity_description.ctx, ) @property def native_value(self) -> StateType: """Return the state of the device.""" - data = self.coordinator.data.user - for element in self.entity_description.value_path: - data = data[element] - return cast(StateType, data) + + return self.entity_description.value_fn(self.coordinator.data.user) -class HabitipyTaskSensor( - CoordinatorEntity[HabiticaDataUpdateCoordinator], SensorEntity -): +class HabitipyTaskSensor(HabiticaBase, SensorEntity): """A Habitica task sensor.""" - def __init__(self, name, task_name, coordinator, entry): - """Initialize a generic Habitica task.""" - super().__init__(coordinator) - self._name = name - self._task_name = task_name - self._task_type = TASKS_TYPES[task_name] - self._state = None - self._attr_unique_id = f"{entry.unique_id}_{task_name}" - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - manufacturer=MANUFACTURER, - model=NAME, - name=entry.data[CONF_NAME], - configuration_url=entry.data[CONF_URL], - identifiers={(DOMAIN, entry.unique_id)}, - ) + entity_description: HabitipyTaskSensorEntityDescription @property - def icon(self): - """Return the icon to use in the frontend, if any.""" - return self._task_type.icon - - @property - def name(self): - """Return the name of the task.""" - return f"{DOMAIN}_{self._name}_{self._task_name}" - - @property - def native_value(self): + def native_value(self) -> StateType: """Return the state of the device.""" - return len( - [ - task - for task in self.coordinator.data.tasks - if task.get("type") in self._task_type.path - and not task.get("completed") - ] - ) + + return len(self.entity_description.value_fn(self.coordinator.data.tasks)) @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> Mapping[str, Any] | None: """Return the state attributes of all user tasks.""" attrs = {} # Map tasks to TASKS_MAP - for received_task in self.coordinator.data.tasks: - if received_task.get("type") in self._task_type.path: - task_id = received_task[TASKS_MAP_ID] - task = {} - for map_key, map_value in TASKS_MAP.items(): - if value := received_task.get(map_value): - task[map_key] = value - attrs[task_id] = task + for received_task in self.entity_description.value_fn( + self.coordinator.data.tasks + ): + task_id = received_task[TASKS_MAP_ID] + task = {} + for map_key, map_value in TASKS_MAP.items(): + if value := received_task.get(map_value): + task[map_key] = value + attrs[task_id] = task return attrs - @property - def native_unit_of_measurement(self): - """Return the unit the value is expressed in.""" - return self._task_type.unit - async def async_added_to_hass(self) -> None: """Raise issue when entity is registered and was not disabled.""" if TYPE_CHECKING: @@ -297,19 +282,20 @@ class HabitipyTaskSensor( ): if ( self.enabled - and self._task_name in ("todos", "dailys") + and self.entity_description.key + in (HabitipySensorEntity.TODOS, HabitipySensorEntity.DAILIES) and entity_used_in(self.hass, entity_id) ): async_create_issue( self.hass, DOMAIN, - f"deprecated_task_entity_{self._task_name}", + f"deprecated_task_entity_{self.entity_description.key}", breaks_in_ha_version="2025.2.0", is_fixable=False, severity=IssueSeverity.WARNING, translation_key="deprecated_task_entity", translation_placeholders={ - "task_name": self._task_name, + "task_name": str(self.name), "entity": entity_id, }, ) @@ -317,6 +303,6 @@ class HabitipyTaskSensor( async_delete_issue( self.hass, DOMAIN, - f"deprecated_task_entity_{self._task_name}", + f"deprecated_task_entity_{self.entity_description.key}", ) await super().async_added_to_hass() diff --git a/homeassistant/components/habitica/strings.json b/homeassistant/components/habitica/strings.json index 9edb6e3ee36..a2c93391503 100644 --- a/homeassistant/components/habitica/strings.json +++ b/homeassistant/components/habitica/strings.json @@ -70,6 +70,18 @@ "wizard": "Mage", "rogue": "Rogue" } + }, + "todos": { + "name": "To-Do's" + }, + "dailys": { + "name": "Dailies" + }, + "habits": { + "name": "Habits" + }, + "rewards": { + "name": "Rewards" } }, "switch": { diff --git a/homeassistant/components/habitica/switch.py b/homeassistant/components/habitica/switch.py index e75a6cdb352..c83d2332030 100644 --- a/homeassistant/components/habitica/switch.py +++ b/homeassistant/components/habitica/switch.py @@ -5,22 +5,19 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from enum import StrEnum -from typing import TYPE_CHECKING, Any +from typing import Any from homeassistant.components.switch import ( SwitchDeviceClass, SwitchEntity, SwitchEntityDescription, ) -from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import HabiticaConfigEntry -from .const import DOMAIN, MANUFACTURER, NAME from .coordinator import HabiticaData, HabiticaDataUpdateCoordinator +from .entity import HabiticaBase @dataclass(kw_only=True, frozen=True) @@ -64,34 +61,11 @@ async def async_setup_entry( ) -class HabiticaSwitch(CoordinatorEntity[HabiticaDataUpdateCoordinator], SwitchEntity): +class HabiticaSwitch(HabiticaBase, SwitchEntity): """Representation of a Habitica Switch.""" - _attr_has_entity_name = True entity_description: HabiticaSwitchEntityDescription - def __init__( - self, - coordinator: HabiticaDataUpdateCoordinator, - entity_description: HabiticaSwitchEntityDescription, - ) -> None: - """Initialize a Habitica switch.""" - super().__init__(coordinator) - if TYPE_CHECKING: - assert coordinator.config_entry.unique_id - self.entity_description = entity_description - self._attr_unique_id = ( - f"{coordinator.config_entry.unique_id}_{entity_description.key}" - ) - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - manufacturer=MANUFACTURER, - model=NAME, - name=coordinator.config_entry.data[CONF_NAME], - configuration_url=coordinator.config_entry.data[CONF_URL], - identifiers={(DOMAIN, coordinator.config_entry.unique_id)}, - ) - @property def is_on(self) -> bool | None: """Return the state of the device.""" diff --git a/homeassistant/components/habitica/todo.py b/homeassistant/components/habitica/todo.py index 01a2a9aed55..55465c87c8c 100644 --- a/homeassistant/components/habitica/todo.py +++ b/homeassistant/components/habitica/todo.py @@ -15,17 +15,16 @@ from homeassistant.components.todo import ( TodoListEntity, TodoListEntityFeature, ) -from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util from . import HabiticaConfigEntry -from .const import ASSETS_URL, DOMAIN, MANUFACTURER, NAME +from .const import ASSETS_URL, DOMAIN from .coordinator import HabiticaDataUpdateCoordinator +from .entity import HabiticaBase from .util import next_due_date @@ -63,35 +62,16 @@ async def async_setup_entry( ) -class BaseHabiticaListEntity( - CoordinatorEntity[HabiticaDataUpdateCoordinator], TodoListEntity -): +class BaseHabiticaListEntity(HabiticaBase, TodoListEntity): """Representation of Habitica task lists.""" - _attr_has_entity_name = True - def __init__( self, coordinator: HabiticaDataUpdateCoordinator, - key: HabiticaTodoList, ) -> None: """Initialize HabiticaTodoListEntity.""" - entry = coordinator.config_entry - if TYPE_CHECKING: - assert entry.unique_id - super().__init__(coordinator) - self._attr_unique_id = f"{entry.unique_id}_{key}" - self._attr_translation_key = key - self.idx = key - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - manufacturer=MANUFACTURER, - model=NAME, - name=entry.data[CONF_NAME], - configuration_url=entry.data[CONF_URL], - identifiers={(DOMAIN, entry.unique_id)}, - ) + super().__init__(coordinator, self.entity_description) async def async_delete_todo_items(self, uids: list[str]) -> None: """Delete Habitica tasks.""" @@ -101,7 +81,7 @@ class BaseHabiticaListEntity( except ClientResponseError as e: raise ServiceValidationError( translation_domain=DOMAIN, - translation_key=f"delete_{self.idx}_failed", + translation_key=f"delete_{self.entity_description.key}_failed", ) from e await self.coordinator.async_refresh() @@ -129,7 +109,7 @@ class BaseHabiticaListEntity( except ClientResponseError as e: raise ServiceValidationError( translation_domain=DOMAIN, - translation_key=f"move_{self.idx}_item_failed", + translation_key=f"move_{self.entity_description.key}_item_failed", translation_placeholders={"pos": str(pos)}, ) from e @@ -145,7 +125,9 @@ class BaseHabiticaListEntity( assert current_item assert item.due - if self.idx is HabiticaTodoList.TODOS: # Only todos support a due date. + if ( + self.entity_description.key is HabiticaTodoList.TODOS + ): # Only todos support a due date. date = item.due.isoformat() else: date = None @@ -159,7 +141,7 @@ class BaseHabiticaListEntity( except ClientResponseError as e: raise ServiceValidationError( translation_domain=DOMAIN, - translation_key=f"update_{self.idx}_item_failed", + translation_key=f"update_{self.entity_description.key}_item_failed", translation_placeholders={"name": item.summary or ""}, ) from e @@ -185,7 +167,7 @@ class BaseHabiticaListEntity( except ClientResponseError as e: raise ServiceValidationError( translation_domain=DOMAIN, - translation_key=f"score_{self.idx}_item_failed", + translation_key=f"score_{self.entity_description.key}_item_failed", translation_placeholders={"name": item.summary or ""}, ) from e @@ -212,10 +194,10 @@ class HabiticaTodosListEntity(BaseHabiticaListEntity): | TodoListEntityFeature.SET_DUE_DATE_ON_ITEM | TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM ) - - def __init__(self, coordinator: HabiticaDataUpdateCoordinator) -> None: - """Initialize HabiticaTodosListEntity.""" - super().__init__(coordinator, HabiticaTodoList.TODOS) + entity_description = EntityDescription( + key=HabiticaTodoList.TODOS, + translation_key=HabiticaTodoList.TODOS, + ) @property def todo_items(self) -> list[TodoItem]: @@ -258,7 +240,7 @@ class HabiticaTodosListEntity(BaseHabiticaListEntity): except ClientResponseError as e: raise ServiceValidationError( translation_domain=DOMAIN, - translation_key=f"create_{self.idx}_item_failed", + translation_key=f"create_{self.entity_description.key}_item_failed", translation_placeholders={"name": item.summary or ""}, ) from e @@ -274,10 +256,10 @@ class HabiticaDailiesListEntity(BaseHabiticaListEntity): | TodoListEntityFeature.SET_DUE_DATE_ON_ITEM | TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM ) - - def __init__(self, coordinator: HabiticaDataUpdateCoordinator) -> None: - """Initialize HabiticaDailiesListEntity.""" - super().__init__(coordinator, HabiticaTodoList.DAILIES) + entity_description = EntityDescription( + key=HabiticaTodoList.DAILIES, + translation_key=HabiticaTodoList.DAILIES, + ) @property def todo_items(self) -> list[TodoItem]: