refactor habitica sensors, add strings and icon translations

This commit is contained in:
tr4nt0r
2024-04-26 01:27:28 +00:00
parent 98eb9a4067
commit c6bd09a3e7
6 changed files with 194 additions and 34 deletions

View File

@@ -550,8 +550,8 @@ build.json @home-assistant/supervisor
/tests/components/group/ @home-assistant/core /tests/components/group/ @home-assistant/core
/homeassistant/components/guardian/ @bachya /homeassistant/components/guardian/ @bachya
/tests/components/guardian/ @bachya /tests/components/guardian/ @bachya
/homeassistant/components/habitica/ @ASMfreaK @leikoilja /homeassistant/components/habitica/ @ASMfreaK @leikoilja @tr4nt0r
/tests/components/habitica/ @ASMfreaK @leikoilja /tests/components/habitica/ @ASMfreaK @leikoilja @tr4nt0r
/homeassistant/components/hardkernel/ @home-assistant/core /homeassistant/components/hardkernel/ @home-assistant/core
/tests/components/hardkernel/ @home-assistant/core /tests/components/hardkernel/ @home-assistant/core
/homeassistant/components/hardware/ @home-assistant/core /homeassistant/components/hardware/ @home-assistant/core

View File

@@ -15,3 +15,6 @@ ATTR_ARGS = "args"
# event constants # event constants
EVENT_API_CALL_SUCCESS = f"{DOMAIN}_{SERVICE_API_CALL}_success" EVENT_API_CALL_SUCCESS = f"{DOMAIN}_{SERVICE_API_CALL}_success"
ATTR_DATA = "data" ATTR_DATA = "data"
MANUFACTURER = "HabitRPG, Inc."
NAME = "Habitica"

View File

@@ -1,5 +1,51 @@
{ {
"services": { "services": {
"api_call": "mdi:console" "api_call": "mdi:console"
},
"entity": {
"sensor": {
"name": {
"default": "mdi:account-circle"
},
"hp": {
"default": "mdi:heart",
"state": {
"0": "mdi:skull-outline"
}
},
"maxhealth": {
"default": "mdi:heart"
},
"mp": {
"default": "mdi:flask",
"state": {
"0": "mdi:flask-empty-outline"
}
},
"maxmp": {
"default": "mdi:flask"
},
"exp": {
"default": "mdi:star-four-points"
},
"tonextlevel": {
"default": "mdi:star-four-points"
},
"lvl": {
"default": "mdi:crown-circle"
},
"gp": {
"default": "mdi:sack"
},
"class": {
"default": "mdi:sword",
"state": {
"warrior": "mdi:sword",
"healer": "mdi:shield",
"wizard": "mdi:wizard-hat",
"rogue": "mdi:ninja"
}
}
}
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"domain": "habitica", "domain": "habitica",
"name": "Habitica", "name": "Habitica",
"codeowners": ["@ASMfreaK", "@leikoilja"], "codeowners": ["@ASMfreaK", "@leikoilja", "@tr4nt0r"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/habitica", "documentation": "https://www.home-assistant.io/integrations/habitica",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",

View File

@@ -3,20 +3,22 @@
from __future__ import annotations from __future__ import annotations
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from http import HTTPStatus from http import HTTPStatus
import logging import logging
from aiohttp import ClientResponseError from aiohttp import ClientResponseError
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME from homeassistant.const import CONF_NAME, CONF_URL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import Throttle from homeassistant.util import Throttle
from .const import DOMAIN from .const import DOMAIN, MANUFACTURER, NAME
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -39,6 +41,78 @@ SENSORS_TYPES = {
"class": SensorType("Class", "mdi:sword", None, ["stats", "class"]), "class": SensorType("Class", "mdi:sword", None, ["stats", "class"]),
} }
@dataclass(kw_only=True, frozen=True)
class HabitipySensorEntityDescription(SensorEntityDescription):
"""Habitipy Sensor Description."""
value_path: list[str]
SENSOR_DESCRIPTIONS: dict[str, HabitipySensorEntityDescription] = {
"name": HabitipySensorEntityDescription(
key="name",
translation_key="name",
value_path=["profile", "name"],
),
"hp": HabitipySensorEntityDescription(
key="hp",
translation_key="hp",
native_unit_of_measurement="HP",
suggested_display_precision=0,
value_path=["stats", "hp"],
),
"maxHealth": HabitipySensorEntityDescription(
key="maxHealth",
translation_key="maxhealth",
native_unit_of_measurement="HP",
value_path=["stats", "maxHealth"],
),
"mp": HabitipySensorEntityDescription(
key="mp",
translation_key="mp",
native_unit_of_measurement="MP",
suggested_display_precision=0,
value_path=["stats", "mp"],
),
"maxMP": HabitipySensorEntityDescription(
key="maxMP",
translation_key="maxmp",
native_unit_of_measurement="MP",
value_path=["stats", "maxMP"],
),
"exp": HabitipySensorEntityDescription(
key="exp",
translation_key="exp",
native_unit_of_measurement="XP",
value_path=["stats", "exp"],
),
"toNextLevel": HabitipySensorEntityDescription(
key="toNextLevel",
translation_key="tonextlevel",
native_unit_of_measurement="XP",
value_path=["stats", "toNextLevel"],
),
"lvl": HabitipySensorEntityDescription(
key="lvl",
translation_key="lvl",
native_unit_of_measurement="Lvl",
value_path=["stats", "lvl"],
),
"gp": HabitipySensorEntityDescription(
key="gp",
translation_key="gp",
native_unit_of_measurement="🜚", # alchemy symbol for gold
suggested_display_precision=2,
value_path=["stats", "gp"],
),
"class": HabitipySensorEntityDescription(
key="class",
translation_key="class",
value_path=["stats", "class"],
),
}
TASKS_TYPES = { TASKS_TYPES = {
"habits": SensorType( "habits": SensorType(
"Habits", "mdi:clipboard-list-outline", "n_of_tasks", ["habits"] "Habits", "mdi:clipboard-list-outline", "n_of_tasks", ["habits"]
@@ -92,7 +166,8 @@ async def async_setup_entry(
await sensor_data.update() await sensor_data.update()
entities: list[SensorEntity] = [ entities: list[SensorEntity] = [
HabitipySensor(name, sensor_type, sensor_data) for sensor_type in SENSORS_TYPES HabitipySensor(sensor_data, description, config_entry)
for description in SENSOR_DESCRIPTIONS.values()
] ]
entities.extend( entities.extend(
HabitipyTaskSensor(name, task_type, sensor_data) for task_type in TASKS_TYPES HabitipyTaskSensor(name, task_type, sensor_data) for task_type in TASKS_TYPES
@@ -153,41 +228,37 @@ class HabitipyData:
class HabitipySensor(SensorEntity): class HabitipySensor(SensorEntity):
"""A generic Habitica sensor.""" """A generic Habitica sensor."""
def __init__(self, name, sensor_name, updater): _attr_has_entity_name = True
entity_description: HabitipySensorEntityDescription
def __init__(
self,
updater,
entity_description: HabitipySensorEntityDescription,
entry: ConfigEntry,
) -> None:
"""Initialize a generic Habitica sensor.""" """Initialize a generic Habitica sensor."""
self._name = name super().__init__()
self._sensor_name = sensor_name assert entry.unique_id
self._sensor_type = SENSORS_TYPES[sensor_name]
self._state = None
self._updater = updater self._updater = updater
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)},
)
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update Condition and Forecast.""" """Update Condition and Forecast."""
await self._updater.update() await self._updater.update()
data = self._updater.data data = self._updater.data
for element in self._sensor_type.path: for element in self.entity_description.value_path:
data = data[element] data = data[element]
self._state = data self._attr_native_value = data
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
return self._sensor_type.icon
@property
def name(self):
"""Return the name of the sensor."""
return f"{DOMAIN}_{self._name}_{self._sensor_name}"
@property
def native_value(self):
"""Return the state of the device."""
return self._state
@property
def native_unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return self._sensor_type.unit
class HabitipyTaskSensor(SensorEntity): class HabitipyTaskSensor(SensorEntity):

View File

@@ -38,5 +38,45 @@
} }
} }
} }
},
"entity": {
"sensor": {
"name": {
"name": "Display name"
},
"hp": {
"name": "Health"
},
"maxhealth": {
"name": "Health max."
},
"mp": {
"name": "Mana"
},
"maxmp": {
"name": "Mana max."
},
"exp": {
"name": "Experience"
},
"tonextlevel": {
"name": "Next Level"
},
"lvl": {
"name": "Level"
},
"gp": {
"name": "Gold"
},
"class": {
"name": "Class",
"state": {
"warrior": "Warrior",
"healer": "Healer",
"wizard": "Mage",
"rogue": "Rogue"
}
}
}
} }
} }