Add button platform to Habitica integration (#121461)

This commit is contained in:
Mr. Bubbles 2024-07-08 09:10:28 +02:00 committed by GitHub
parent cf4bd7fd1c
commit ea65ff5876
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 185 additions and 1 deletions

View File

@ -82,7 +82,7 @@ INSTANCE_LIST_SCHEMA = vol.All(
)
CONFIG_SCHEMA = vol.Schema({DOMAIN: INSTANCE_LIST_SCHEMA}, extra=vol.ALLOW_EXTRA)
PLATFORMS = [Platform.SENSOR, Platform.TODO]
PLATFORMS = [Platform.BUTTON, Platform.SENSOR, Platform.TODO]
SERVICE_API_CALL_SCHEMA = vol.Schema(
{

View File

@ -0,0 +1,150 @@
"""Habitica button platform."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
from http import HTTPStatus
from typing import TYPE_CHECKING, 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 .coordinator import HabiticaData, HabiticaDataUpdateCoordinator
@dataclass(kw_only=True, frozen=True)
class HabiticaButtonEntityDescription(ButtonEntityDescription):
"""Describes Habitica button entity."""
press_fn: Callable[[HabiticaDataUpdateCoordinator], Any]
available_fn: Callable[[HabiticaData], bool] | None = None
class HabitipyButtonEntity(StrEnum):
"""Habitica button entities."""
RUN_CRON = "run_cron"
BUY_HEALTH_POTION = "buy_health_potion"
ALLOCATE_ALL_STAT_POINTS = "allocate_all_stat_points"
REVIVE = "revive"
BUTTON_DESCRIPTIONS: tuple[HabiticaButtonEntityDescription, ...] = (
HabiticaButtonEntityDescription(
key=HabitipyButtonEntity.RUN_CRON,
translation_key=HabitipyButtonEntity.RUN_CRON,
press_fn=lambda coordinator: coordinator.api.cron.post(),
available_fn=lambda data: data.user["needsCron"],
),
HabiticaButtonEntityDescription(
key=HabitipyButtonEntity.BUY_HEALTH_POTION,
translation_key=HabitipyButtonEntity.BUY_HEALTH_POTION,
press_fn=(
lambda coordinator: coordinator.api["user"]["buy-health-potion"].post()
),
available_fn=(
lambda data: data.user["stats"]["gp"] >= 25
and data.user["stats"]["hp"] < 50
),
),
HabiticaButtonEntityDescription(
key=HabitipyButtonEntity.ALLOCATE_ALL_STAT_POINTS,
translation_key=HabitipyButtonEntity.ALLOCATE_ALL_STAT_POINTS,
press_fn=lambda coordinator: coordinator.api["user"]["allocate-now"].post(),
available_fn=(
lambda data: data.user["preferences"].get("automaticAllocation") is True
and data.user["stats"]["points"] > 0
),
),
HabiticaButtonEntityDescription(
key=HabitipyButtonEntity.REVIVE,
translation_key=HabitipyButtonEntity.REVIVE,
press_fn=lambda coordinator: coordinator.api["user"]["revive"].post(),
available_fn=lambda data: data.user["stats"]["hp"] == 0,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: HabiticaConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up buttons from a config entry."""
coordinator = entry.runtime_data
async_add_entities(
HabiticaButton(coordinator, description) for description in BUTTON_DESCRIPTIONS
)
class HabiticaButton(CoordinatorEntity[HabiticaDataUpdateCoordinator], 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:
await self.entity_description.press_fn(self.coordinator)
except ClientResponseError as e:
if e.status == HTTPStatus.TOO_MANY_REQUESTS:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="setup_rate_limit_exception",
) from e
if e.status == HTTPStatus.UNAUTHORIZED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="service_call_unallowed",
) from e
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="service_call_exception",
) from e
else:
await self.coordinator.async_refresh()
@property
def available(self) -> bool:
"""Is entity available."""
if not super().available:
return False
if self.entity_description.available_fn:
return self.entity_description.available_fn(self.coordinator.data)
return True

View File

@ -8,6 +8,20 @@
"default": "mdi:calendar-month"
}
},
"button": {
"run_cron": {
"default": "mdi:weather-sunset"
},
"buy_health_potion": {
"default": "mdi:flask-round-bottom"
},
"allocate_all_stat_points": {
"default": "mdi:chart-box-outline"
},
"revive": {
"default": "mdi:grave-stone"
}
},
"sensor": {
"display_name": {
"default": "mdi:account-circle"

View File

@ -20,6 +20,20 @@
}
},
"entity": {
"button": {
"run_cron": {
"name": "Start my day"
},
"buy_health_potion": {
"name": "Buy a health potion"
},
"allocate_all_stat_points": {
"name": "Allocate all stat points"
},
"revive": {
"name": "Revive from death"
}
},
"sensor": {
"display_name": {
"name": "Display name"
@ -94,6 +108,12 @@
},
"setup_rate_limit_exception": {
"message": "Currently rate limited, try again later"
},
"service_call_unallowed": {
"message": "Unable to carry out this action, because the required conditions are not met"
},
"service_call_exception": {
"message": "Unable to connect to Habitica, try again later"
}
},
"issues": {