Add entity base class in Habitica integration (#121320)

* Sensor refactoring

* Change todo entities to use common base entity

* Requested changes

* Update button platform to use base class

* Update swtich platform to use base entity class
This commit is contained in:
Mr. Bubbles 2024-07-08 10:18:09 +02:00 committed by GitHub
parent 6350c5479b
commit 00aafc0cf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 205 additions and 220 deletions

View File

@ -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:

View File

@ -21,3 +21,5 @@ MANUFACTURER = "HabitRPG, Inc."
NAME = "Habitica"
ADDITIONAL_USER_FIELDS: set[str] = {"lastCron"}
UNIT_TASKS = "tasks"

View File

@ -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)},
)

View File

@ -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": {

View File

@ -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()

View File

@ -70,6 +70,18 @@
"wizard": "Mage",
"rogue": "Rogue"
}
},
"todos": {
"name": "To-Do's"
},
"dailys": {
"name": "Dailies"
},
"habits": {
"name": "Habits"
},
"rewards": {
"name": "Rewards"
}
},
"switch": {

View File

@ -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."""

View File

@ -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]: