Add pending damage and pending quest items sensors (#145449)

Add pending damage and quest items sensors
This commit is contained in:
Manu 2025-05-22 23:10:49 +02:00 committed by GitHub
parent a15572bb8c
commit 2f318927bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 157 additions and 1 deletions

View File

@ -159,6 +159,12 @@
}, },
"quest_scrolls": { "quest_scrolls": {
"default": "mdi:script-text-outline" "default": "mdi:script-text-outline"
},
"pending_damage": {
"default": "mdi:sword"
},
"pending_quest_items": {
"default": "mdi:sack"
} }
}, },
"switch": { "switch": {

View File

@ -40,7 +40,13 @@ from homeassistant.util import dt as dt_util
from .const import ASSETS_URL, DOMAIN from .const import ASSETS_URL, DOMAIN
from .coordinator import HabiticaConfigEntry, HabiticaDataUpdateCoordinator from .coordinator import HabiticaConfigEntry, HabiticaDataUpdateCoordinator
from .entity import HabiticaBase from .entity import HabiticaBase
from .util import get_attribute_points, get_attributes_total, inventory_list from .util import (
get_attribute_points,
get_attributes_total,
inventory_list,
pending_damage,
pending_quest_items,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -99,6 +105,8 @@ class HabiticaSensorEntity(StrEnum):
FOOD_TOTAL = "food_total" FOOD_TOTAL = "food_total"
SADDLE = "saddle" SADDLE = "saddle"
QUEST_SCROLLS = "quest_scrolls" QUEST_SCROLLS = "quest_scrolls"
PENDING_DAMAGE = "pending_damage"
PENDING_QUEST_ITEMS = "pending_quest_items"
SENSOR_DESCRIPTIONS: tuple[HabiticaSensorEntityDescription, ...] = ( SENSOR_DESCRIPTIONS: tuple[HabiticaSensorEntityDescription, ...] = (
@ -263,6 +271,18 @@ SENSOR_DESCRIPTIONS: tuple[HabiticaSensorEntityDescription, ...] = (
entity_picture="inventory_quest_scroll_dustbunnies.png", entity_picture="inventory_quest_scroll_dustbunnies.png",
attributes_fn=lambda user, content: inventory_list(user, content, "quests"), attributes_fn=lambda user, content: inventory_list(user, content, "quests"),
), ),
HabiticaSensorEntityDescription(
key=HabiticaSensorEntity.PENDING_DAMAGE,
translation_key=HabiticaSensorEntity.PENDING_DAMAGE,
value_fn=pending_damage,
suggested_display_precision=1,
entity_picture=ha.DAMAGE,
),
HabiticaSensorEntityDescription(
key=HabiticaSensorEntity.PENDING_QUEST_ITEMS,
translation_key=HabiticaSensorEntity.PENDING_QUEST_ITEMS,
value_fn=pending_quest_items,
),
) )

View File

@ -426,6 +426,14 @@
"quest_scrolls": { "quest_scrolls": {
"name": "Quest scrolls", "name": "Quest scrolls",
"unit_of_measurement": "scrolls" "unit_of_measurement": "scrolls"
},
"pending_damage": {
"name": "Pending damage",
"unit_of_measurement": "damage"
},
"pending_quest_items": {
"name": "Pending quest items",
"unit_of_measurement": "items"
} }
}, },
"switch": { "switch": {

View File

@ -162,3 +162,25 @@ def inventory_list(
for k, v in getattr(user.items, item_type, {}).items() for k, v in getattr(user.items, item_type, {}).items()
if k != "Saddle" if k != "Saddle"
} }
def pending_quest_items(user: UserData, content: ContentData) -> int | None:
"""Pending quest items."""
return (
user.party.quest.progress.collectedItems
if user.party.quest.key
and content.quests[user.party.quest.key].collect is not None
else None
)
def pending_damage(user: UserData, content: ContentData) -> float | None:
"""Pending damage."""
return (
user.party.quest.progress.up
if user.party.quest.key
and content.quests[user.party.quest.key].boss is not None
else None
)

View File

@ -1038,6 +1038,106 @@
'state': '880', 'state': '880',
}) })
# --- # ---
# name: test_sensors[sensor.test_user_pending_damage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_user_pending_damage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Pending damage',
'platform': 'habitica',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <HabiticaSensorEntity.PENDING_DAMAGE: 'pending_damage'>,
'unique_id': 'a380546a-94be-4b8e-8a0b-23e0d5c03303_pending_damage',
'unit_of_measurement': 'damage',
})
# ---
# name: test_sensors[sensor.test_user_pending_damage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'entity_picture': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSItMyAtMyAyMiAyMiI+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBpZD0iYSIgZD0iTTEwLjQ2NCAyLjkxN0w4LjIgNS4xOTd2Mi4wMmwyLjI2NC0yLjE3M1YyLjkxN3oiPjwvcGF0aD4KICAgIDwvZGVmcz4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTYgLjMyKSI+CiAgICAgICAgPHBhdGggZmlsbD0iI0YwNjE2NiIgZD0iTTYuMTMgOS4yMDRsMi4xMTEuOTM0Yy4xNzYuMDc4LjI5LjIzNS4zMzMuNDE1LjA3My4zMDQuMjk1IDEuMDEuMzEzIDEuMzg2LjAxLjIxLS4yMTQuMzU2LS40MTQuMjdsLTMuNTI5LTEuNjIzYS41ODIuNTgyIDAgMCAxLS4yNTQtLjI0NEwzIDYuOTU1Yy0uMDktLjE5Mi4wNjMtLjQwNy4yODEtLjM5Ny4zOTEuMDE3IDEuMTEyLjIxOCAxLjQ0NC4zLjE4Ni4wNDUuMzUxLjE1LjQzMi4zMTlsLjk3MyAyLjAyN3oiPjwvcGF0aD4KICAgICAgICA8cGF0aCBmaWxsPSIjODc4MTkwIiBkPSJNMS4wMjQgMTQuMTA3bC45MS44NzUgMi4zNjMtLjE3OS4xMjEtMS40OSAxLjM1Ni0xLjMgMi40NjcgMS4xMjYgMS44NDYtLjQ3Ny0uNzc0LTMuMTk2IDUuMTcxLTQuNjMzLjk5LTQuNTk2aC0uMDAyVi4yMzVsLTQuNzg2Ljk1TDUuODYgNi4xNWwtMy4zMy0uNzQzLS40OTcgMS43NyAxLjE3NCAyLjM3LTEuMzU1IDEuMy0xLjU1Mi4xMTgtLjE4NiAyLjI2Ny45MS44NzV6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0YwNjE2NiIgZD0iTTIuOTc2IDEzLjM2NmwtMS4xODItMS4xMzQgMi45MjMtMi44MDUgMS4xOCAxLjEzNHoiPjwvcGF0aD4KICAgICAgICA8cGF0aCBmaWxsPSIjRjA2MTY2IiBkPSJNMS4xMjYgMTIuODc0bC4wODUtMS4wMzUgMS4wNzgtLjA4MiAxLjE4MiAxLjEzNS0uMDg1IDEuMDM1LTEuMDc4LjA4MnoiPjwvcGF0aD4KICAgICAgICA8cGF0aCBmaWxsPSIjRkZGIiBkPSJNMTEuMzEyIDIuMDg4bC4xIDIuMDQ2IDIuNzAyLTIuNTk1Yy0uMDUtLjA0NS0yLjA4Ni4xOC0yLjgwMi41NSI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNFREVDRUUiIGQ9Ik0xMS4yNjIgMi4xMTNMNS41NTMgNy44NjJsMS40NjMuNDkyIDQuMzk2LTQuMjItLjEtMi4wNDYtLjA1LjAyNU01LjU1MyA3Ljg2MmwtLjA1LjA1Mi42MjIgMS4yOTQuODktLjg1NC0xLjQ2Mi0uNDkyeiI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNFREVDRUUiIGQ9Ik0xMy41NDEgNC4yM2wtMi4xMy0uMDk2IDIuNzAzLTIuNTk0Yy4wNDYuMDQ4LS4xODkgMi4wMDMtLjU3MyAyLjY5Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0UxRTBFMyIgZD0iTTEzLjUxNiA0LjI3OGwtNS45ODkgNS40OC0uNTEyLTEuNDA0IDQuMzk2LTQuMjIgMi4xMy4wOTYtLjAyNS4wNDhNNy41MjcgOS43NThsLS4wNTQuMDQ4LTEuMzQ4LS41OTcuODktLjg1NC41MTIgMS40MDN6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0Q1QzhGRiIgZD0iTTIuMjg5IDExLjc1N2wtLjI1Ljg3OC0uODI5LS43OTZ6TTMuNDY5IDEyLjg5bC0uOTE0LjI0LjgyOC43OTV6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0JEQThGRiIgZD0iTTIuMjg5IDExLjc1N2wxLjE4MiAxLjEzNS0uOTE2LjIzNy0uNTE2LS40OTR6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0JEQThGRiIgZD0iTTEuMTI3IDEyLjg3NmwuOTE0LS4yNC0uODI4LS43OTZ6TTIuMzA3IDE0LjAwOGwuMjUtLjg3Ny44MjkuNzk2eiI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNENUM4RkYiIGQ9Ik0xLjEyNyAxMi44NzZMMi4zMSAxNC4wMWwuMjQ3LS44OC0uNTE2LS40OTV6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0IzNjIxMyIgZD0iTTQuOSAxMS41MjNsLTEuMTg0LTEuMTM3LjcxNS0uNjg1IDEuMTg0IDEuMTM2eiI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNFMzhGM0QiIGQ9Ik00LjE4NyAxMi4yMDhsLTEuMTg0LTEuMTM2LjcxNC0uNjg1TDQuOSAxMS41MjN6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0IzNjIxMyIgZD0iTTMuNDczIDEyLjg5NGwtMS4xODQtMS4xMzcuNzE0LS42ODUgMS4xODQgMS4xMzZ6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0MzQzBDNyIgZD0iTTYuMTMyIDkuMjA1bC0uOTc0LTIuMDI3YS41MjYuNTI2IDAgMCAwLS4xNTMtLjE4NS43MTguNzE4IDAgMCAwLS4yNzktLjEzNWMtLjMzMS0uMDgtMS4wNTItLjI4Mi0xLjQ0My0uM2EuMjk1LjI5NSAwIDAgMC0uMjQyLjEwOEw0LjQ2IDcuODI5IDUuNTAzIDkuODFsLjYzLS42MDVoLS4wMDF6Ij48L3BhdGg+CiAgICAgICAgPHBhdGggZmlsbD0iI0E1QTFBQyIgZD0iTTQuNDYgNy44MjlMMy4wNCA2LjY2NmEuMjcuMjcgMCAwIDAtLjAzOS4yOWwxLjY5IDMuMzg3Yy4wMjkuMDUzLjA2Ni4xLjExLjE0MmwuNzAyLS42NzVMNC40NiA3LjgzeiI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNDM0MwQzciIGQ9Ik04Ljg5MSAxMS45NGMtLjAxOC0uMzc1LS4yMjgtMS4wNjgtLjMxMi0xLjM4NWEuNjY4LjY2OCAwIDAgMC0uMTQtLjI2OC41NC41NCAwIDAgMC0uMTkzLS4xNDdsLTIuMTExLS45MzV2LS4wMDJsLS42MzEuNjA2IDIuMDY0IDEuMDAxIDEuMjExIDEuMzYzYS4yNzUuMjc1IDAgMCAwIC4xMTItLjIzMyI+PC9wYXRoPgogICAgICAgIDxwYXRoIGZpbGw9IiNBNUExQUMiIGQ9Ik03LjU2OCAxMC44MWwxLjIxMSAxLjM2M2EuMy4zIDAgMCAxLS4zMDEuMDM3bC0zLjUzLTEuNjIyYS41ODguNTg4IDAgMCAxLS4xNDctLjEwNWwuNzAzLS42NzQgMi4wNjQgMS4wMDF6Ij48L3BhdGg+CiAgICAgICAgPG1hc2sgaWQ9ImIiIGZpbGw9IiNmZmYiPgogICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNhIj48L3VzZT4KICAgICAgICA8L21hc2s+CiAgICAgICAgPHBhdGggZmlsbD0iI0ZGRiIgZD0iTTkuODI0IDcuNDVoLjMyOVYxLjg2NWgtLjMyOXpNOC4yIDguODYyaC45NzRWMy4yNzdIOC4yeiIgbWFzaz0idXJsKCNiKSI+PC9wYXRoPgogICAgPC9nPgo8L3N2Zz4=',
'friendly_name': 'test-user Pending damage',
'unit_of_measurement': 'damage',
}),
'context': <ANY>,
'entity_id': 'sensor.test_user_pending_damage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_sensors[sensor.test_user_pending_quest_items-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_user_pending_quest_items',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Pending quest items',
'platform': 'habitica',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <HabiticaSensorEntity.PENDING_QUEST_ITEMS: 'pending_quest_items'>,
'unique_id': 'a380546a-94be-4b8e-8a0b-23e0d5c03303_pending_quest_items',
'unit_of_measurement': 'items',
})
# ---
# name: test_sensors[sensor.test_user_pending_quest_items-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'test-user Pending quest items',
'unit_of_measurement': 'items',
}),
'context': <ANY>,
'entity_id': 'sensor.test_user_pending_quest_items',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_sensors[sensor.test_user_perception-entry] # name: test_sensors[sensor.test_user_perception-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({