mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Add get_tasks action to Habitica integration (#127687)
Add get_tasks action
This commit is contained in:
parent
add401ffcf
commit
5726d090b0
@ -31,6 +31,11 @@ ATTR_TASK = "task"
|
||||
ATTR_DIRECTION = "direction"
|
||||
ATTR_TARGET = "target"
|
||||
ATTR_ITEM = "item"
|
||||
ATTR_TYPE = "type"
|
||||
ATTR_PRIORITY = "priority"
|
||||
ATTR_TAG = "tag"
|
||||
ATTR_KEYWORD = "keyword"
|
||||
|
||||
SERVICE_CAST_SKILL = "cast_skill"
|
||||
SERVICE_START_QUEST = "start_quest"
|
||||
SERVICE_ACCEPT_QUEST = "accept_quest"
|
||||
@ -38,6 +43,8 @@ SERVICE_CANCEL_QUEST = "cancel_quest"
|
||||
SERVICE_ABORT_QUEST = "abort_quest"
|
||||
SERVICE_REJECT_QUEST = "reject_quest"
|
||||
SERVICE_LEAVE_QUEST = "leave_quest"
|
||||
SERVICE_GET_TASKS = "get_tasks"
|
||||
|
||||
SERVICE_SCORE_HABIT = "score_habit"
|
||||
SERVICE_SCORE_REWARD = "score_reward"
|
||||
|
||||
|
@ -196,6 +196,12 @@
|
||||
},
|
||||
"transformation": {
|
||||
"service": "mdi:flask-round-bottom"
|
||||
},
|
||||
"get_tasks": {
|
||||
"service": "mdi:calendar-export",
|
||||
"sections": {
|
||||
"filter": "mdi:calendar-filter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from aiohttp import ClientError
|
||||
from habiticalib import (
|
||||
@ -13,6 +13,9 @@ from habiticalib import (
|
||||
NotAuthorizedError,
|
||||
NotFoundError,
|
||||
Skill,
|
||||
TaskData,
|
||||
TaskPriority,
|
||||
TaskType,
|
||||
TooManyRequestsError,
|
||||
)
|
||||
import voluptuous as vol
|
||||
@ -36,10 +39,14 @@ from .const import (
|
||||
ATTR_DATA,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_ITEM,
|
||||
ATTR_KEYWORD,
|
||||
ATTR_PATH,
|
||||
ATTR_PRIORITY,
|
||||
ATTR_SKILL,
|
||||
ATTR_TAG,
|
||||
ATTR_TARGET,
|
||||
ATTR_TASK,
|
||||
ATTR_TYPE,
|
||||
DOMAIN,
|
||||
EVENT_API_CALL_SUCCESS,
|
||||
SERVICE_ABORT_QUEST,
|
||||
@ -47,6 +54,7 @@ from .const import (
|
||||
SERVICE_API_CALL,
|
||||
SERVICE_CANCEL_QUEST,
|
||||
SERVICE_CAST_SKILL,
|
||||
SERVICE_GET_TASKS,
|
||||
SERVICE_LEAVE_QUEST,
|
||||
SERVICE_REJECT_QUEST,
|
||||
SERVICE_SCORE_HABIT,
|
||||
@ -96,6 +104,21 @@ SERVICE_TRANSFORMATION_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_GET_TASKS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector(),
|
||||
vol.Optional(ATTR_TYPE): vol.All(
|
||||
cv.ensure_list, [vol.All(vol.Upper, vol.In({x.name for x in TaskType}))]
|
||||
),
|
||||
vol.Optional(ATTR_PRIORITY): vol.All(
|
||||
cv.ensure_list, [vol.All(vol.Upper, vol.In({x.name for x in TaskPriority}))]
|
||||
),
|
||||
vol.Optional(ATTR_TASK): vol.All(cv.ensure_list, [str]),
|
||||
vol.Optional(ATTR_TAG): vol.All(cv.ensure_list, [str]),
|
||||
vol.Optional(ATTR_KEYWORD): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
SKILL_MAP = {
|
||||
"pickpocket": Skill.PICKPOCKET,
|
||||
"backstab": Skill.BACKSTAB,
|
||||
@ -403,6 +426,52 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
||||
else:
|
||||
return asdict(response.data)
|
||||
|
||||
async def get_tasks(call: ServiceCall) -> ServiceResponse:
|
||||
"""Get tasks action."""
|
||||
|
||||
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
|
||||
coordinator = entry.runtime_data
|
||||
response: list[TaskData] = coordinator.data.tasks
|
||||
|
||||
if types := {TaskType[x] for x in call.data.get(ATTR_TYPE, [])}:
|
||||
response = [task for task in response if task.Type in types]
|
||||
|
||||
if priority := {TaskPriority[x] for x in call.data.get(ATTR_PRIORITY, [])}:
|
||||
response = [task for task in response if task.priority in priority]
|
||||
|
||||
if tasks := call.data.get(ATTR_TASK):
|
||||
response = [
|
||||
task
|
||||
for task in response
|
||||
if str(task.id) in tasks or task.alias in tasks or task.text in tasks
|
||||
]
|
||||
|
||||
if tags := call.data.get(ATTR_TAG):
|
||||
tag_ids = {
|
||||
tag.id
|
||||
for tag in coordinator.data.user.tags
|
||||
if (tag.name and tag.name.lower())
|
||||
in (tag.lower() for tag in tags) # Case-insensitive matching
|
||||
and tag.id
|
||||
}
|
||||
|
||||
response = [
|
||||
task
|
||||
for task in response
|
||||
if any(tag_id in task.tags for tag_id in tag_ids if task.tags)
|
||||
]
|
||||
if keyword := call.data.get(ATTR_KEYWORD):
|
||||
keyword = keyword.lower()
|
||||
response = [
|
||||
task
|
||||
for task in response
|
||||
if (task.text and keyword in task.text.lower())
|
||||
or (task.notes and keyword in task.notes.lower())
|
||||
or any(keyword in item.text.lower() for item in task.checklist)
|
||||
]
|
||||
result: dict[str, Any] = {"tasks": response}
|
||||
return result
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_API_CALL,
|
||||
@ -440,3 +509,10 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
||||
schema=SERVICE_TRANSFORMATION_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_TASKS,
|
||||
get_tasks,
|
||||
schema=SERVICE_GET_TASKS_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
@ -94,3 +94,49 @@ transformation:
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
get_tasks:
|
||||
fields:
|
||||
config_entry: *config_entry
|
||||
filter:
|
||||
collapsed: true
|
||||
fields:
|
||||
type:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "habit"
|
||||
- "daily"
|
||||
- "todo"
|
||||
- "reward"
|
||||
mode: dropdown
|
||||
translation_key: "type"
|
||||
multiple: true
|
||||
sort: true
|
||||
priority:
|
||||
required: false
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "trivial"
|
||||
- "easy"
|
||||
- "medium"
|
||||
- "hard"
|
||||
mode: dropdown
|
||||
translation_key: "priority"
|
||||
multiple: true
|
||||
sort: false
|
||||
task:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiple: true
|
||||
tag:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiple: true
|
||||
keyword:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
|
@ -3,6 +3,7 @@
|
||||
"todos": "To-Do's",
|
||||
"dailies": "Dailies",
|
||||
"config_entry_name": "Select character",
|
||||
"task_name": "Task name",
|
||||
"unit_tasks": "tasks",
|
||||
"unit_health_points": "HP",
|
||||
"unit_mana_points": "MP",
|
||||
@ -444,7 +445,7 @@
|
||||
"description": "Select the skill or spell you want to cast on the task. Only skills corresponding to your character's class can be used."
|
||||
},
|
||||
"task": {
|
||||
"name": "Task name",
|
||||
"name": "[%key:component::habitica::common::task_name%]",
|
||||
"description": "The name (or task ID) of the task you want to target with the skill or spell."
|
||||
}
|
||||
}
|
||||
@ -558,6 +559,42 @@
|
||||
"description": "The name of the character you want to use the transformation item on. You can also specify the players username or user ID."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_tasks": {
|
||||
"name": "Get tasks",
|
||||
"description": "Retrieve tasks from your Habitica character.",
|
||||
"fields": {
|
||||
"config_entry": {
|
||||
"name": "[%key:component::habitica::common::config_entry_name%]",
|
||||
"description": "Choose the Habitica character to retrieve tasks from."
|
||||
},
|
||||
"type": {
|
||||
"name": "Task type",
|
||||
"description": "Filter tasks by type."
|
||||
},
|
||||
"priority": {
|
||||
"name": "Difficulty",
|
||||
"description": "Filter tasks by difficulty."
|
||||
},
|
||||
"task": {
|
||||
"name": "[%key:component::habitica::common::task_name%]",
|
||||
"description": "Select tasks by matching their name (or task ID)."
|
||||
},
|
||||
"tag": {
|
||||
"name": "Tag",
|
||||
"description": "Filter tasks that have one or more of the selected tags."
|
||||
},
|
||||
"keyword": {
|
||||
"name": "Keyword",
|
||||
"description": "Filter tasks by keyword, searching across titles, notes, and checklists."
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"filter": {
|
||||
"name": "Filter options",
|
||||
"description": "Use the optional filters to narrow the returned tasks."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
@ -576,6 +613,22 @@
|
||||
"seafoam": "Seafoam",
|
||||
"shiny_seed": "Shiny seed"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"options": {
|
||||
"daily": "Daily",
|
||||
"habit": "Habit",
|
||||
"todo": "To-do",
|
||||
"reward": "Reward"
|
||||
}
|
||||
},
|
||||
"priority": {
|
||||
"options": {
|
||||
"trivial": "Trivial",
|
||||
"easy": "Easy",
|
||||
"medium": "Medium",
|
||||
"hard": "Hard"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,13 +233,20 @@
|
||||
"startDate": "2024-07-06T22:00:00.000Z",
|
||||
"daysOfMonth": [],
|
||||
"weeksOfMonth": [],
|
||||
"checklist": [],
|
||||
"checklist": [
|
||||
{
|
||||
"completed": false,
|
||||
"id": "c8662c16-8cd3-4104-a3b2-b1e54f61b8ca",
|
||||
"text": "Checklist-item1"
|
||||
}
|
||||
],
|
||||
"reminders": [],
|
||||
"createdAt": "2024-07-07T17:51:53.268Z",
|
||||
"updatedAt": "2024-09-21T22:24:20.154Z",
|
||||
"userId": "5f359083-ef78-4af0-985a-0b2c6d05797c",
|
||||
"isDue": true,
|
||||
"id": "564b9ac9-c53d-4638-9e7f-1cd96fe19baa"
|
||||
"id": "564b9ac9-c53d-4638-9e7f-1cd96fe19baa",
|
||||
"alias": "alias_zahnseide_benutzen"
|
||||
},
|
||||
{
|
||||
"_id": "f2c85972-1a19-4426-bc6d-ce3337b9d99f",
|
||||
@ -386,11 +393,17 @@
|
||||
"history": [],
|
||||
"completed": false,
|
||||
"collapseChecklist": false,
|
||||
"checklist": [],
|
||||
"checklist": [
|
||||
{
|
||||
"completed": true,
|
||||
"id": "c8662c16-8cd3-4104-a3b2-b1e54f61b8ca",
|
||||
"text": "Checklist-item1"
|
||||
}
|
||||
],
|
||||
"type": "daily",
|
||||
"text": "Fitnessstudio besuchen",
|
||||
"notes": "Ein einstündiges Workout im Fitnessstudio absolvieren.",
|
||||
"tags": ["51076966-2970-4b40-b6ba-d58c6a756dd7"],
|
||||
"tags": ["6aa65cbb-dc08-4fdd-9a66-7dedb7ba4cab"],
|
||||
"value": 0,
|
||||
"priority": 2,
|
||||
"attribute": "str",
|
||||
@ -416,7 +429,10 @@
|
||||
"type": "todo",
|
||||
"text": "Buch zu Ende lesen",
|
||||
"notes": "Das Buch, das du angefangen hast, bis zum Wochenende fertig lesen.",
|
||||
"tags": [],
|
||||
"tags": [
|
||||
"20409521-c096-447f-9a90-23e8da615710",
|
||||
"8515e4ae-2f4b-455a-b4a4-8939e04b1bfd"
|
||||
],
|
||||
"value": 0,
|
||||
"priority": 1,
|
||||
"attribute": "str",
|
||||
|
@ -67,6 +67,36 @@
|
||||
},
|
||||
"_id": "94cd398c-2240-4320-956e-6d345cf2c0de"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"id": "8515e4ae-2f4b-455a-b4a4-8939e04b1bfd",
|
||||
"name": "Arbeit"
|
||||
},
|
||||
{
|
||||
"id": "6aa65cbb-dc08-4fdd-9a66-7dedb7ba4cab",
|
||||
"name": "Training"
|
||||
},
|
||||
{
|
||||
"id": "20409521-c096-447f-9a90-23e8da615710",
|
||||
"name": "Gesundheit + Wohlbefinden"
|
||||
},
|
||||
{
|
||||
"id": "2ac458af-0833-4f3f-bf04-98a0c33ef60b",
|
||||
"name": "Schule"
|
||||
},
|
||||
{
|
||||
"id": "1bcb1a0f-4d05-4087-8223-5ea779e258b0",
|
||||
"name": "Teams"
|
||||
},
|
||||
{
|
||||
"id": "b2780f82-b3b5-49a3-a677-48f2c8c7e3bb",
|
||||
"name": "Hausarbeiten"
|
||||
},
|
||||
{
|
||||
"id": "3450351f-1323-4c7e-9fd2-0cdff25b3ce0",
|
||||
"name": "Kreativität"
|
||||
}
|
||||
],
|
||||
"needsCron": true,
|
||||
"lastCron": "2024-09-21T22:01:55.586Z",
|
||||
"id": "a380546a-94be-4b8e-8a0b-23e0d5c03303",
|
||||
|
@ -198,7 +198,7 @@
|
||||
}),
|
||||
'start_date': '2024-09-21T22:00:00+00:00',
|
||||
'tags': list([
|
||||
'51076966-2970-4b40-b6ba-d58c6a756dd7',
|
||||
'6aa65cbb-dc08-4fdd-9a66-7dedb7ba4cab',
|
||||
]),
|
||||
'text': 'Fitnessstudio besuchen',
|
||||
'type': 'daily',
|
||||
@ -1553,6 +1553,10 @@
|
||||
'th': False,
|
||||
'w': True,
|
||||
}),
|
||||
'tags': list([
|
||||
'20409521-c096-447f-9a90-23e8da615710',
|
||||
'8515e4ae-2f4b-455a-b4a4-8939e04b1bfd',
|
||||
]),
|
||||
'text': 'Buch zu Ende lesen',
|
||||
'type': 'todo',
|
||||
}),
|
||||
|
6229
tests/components/habitica/snapshots/test_services.ambr
Normal file
6229
tests/components/habitica/snapshots/test_services.ambr
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,19 +7,25 @@ from uuid import UUID
|
||||
|
||||
from habiticalib import Direction, Skill
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.habitica.const import (
|
||||
ATTR_CONFIG_ENTRY,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_ITEM,
|
||||
ATTR_KEYWORD,
|
||||
ATTR_PRIORITY,
|
||||
ATTR_SKILL,
|
||||
ATTR_TAG,
|
||||
ATTR_TARGET,
|
||||
ATTR_TASK,
|
||||
ATTR_TYPE,
|
||||
DOMAIN,
|
||||
SERVICE_ABORT_QUEST,
|
||||
SERVICE_ACCEPT_QUEST,
|
||||
SERVICE_CANCEL_QUEST,
|
||||
SERVICE_CAST_SKILL,
|
||||
SERVICE_GET_TASKS,
|
||||
SERVICE_LEAVE_QUEST,
|
||||
SERVICE_REJECT_QUEST,
|
||||
SERVICE_SCORE_HABIT,
|
||||
@ -775,3 +781,67 @@ async def test_transformation_exceptions(
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service_data"),
|
||||
[
|
||||
{},
|
||||
{ATTR_TYPE: ["daily"]},
|
||||
{ATTR_TYPE: ["habit"]},
|
||||
{ATTR_TYPE: ["todo"]},
|
||||
{ATTR_TYPE: ["reward"]},
|
||||
{ATTR_TYPE: ["daily", "habit"]},
|
||||
{ATTR_TYPE: ["todo", "reward"]},
|
||||
{ATTR_PRIORITY: "trivial"},
|
||||
{ATTR_PRIORITY: "easy"},
|
||||
{ATTR_PRIORITY: "medium"},
|
||||
{ATTR_PRIORITY: "hard"},
|
||||
{ATTR_TASK: ["Zahnseide benutzen", "Eine kurze Pause machen"]},
|
||||
{ATTR_TASK: ["f2c85972-1a19-4426-bc6d-ce3337b9d99f"]},
|
||||
{ATTR_TASK: ["alias_zahnseide_benutzen"]},
|
||||
{ATTR_TAG: ["Training", "Gesundheit + Wohlbefinden"]},
|
||||
{ATTR_KEYWORD: "gewohnheit"},
|
||||
{ATTR_TAG: ["Home Assistant"]},
|
||||
],
|
||||
ids=[
|
||||
"all_tasks",
|
||||
"only dailies",
|
||||
"only habits",
|
||||
"only todos",
|
||||
"only rewards",
|
||||
"only dailies and habits",
|
||||
"only todos and rewards",
|
||||
"trivial tasks",
|
||||
"easy tasks",
|
||||
"medium tasks",
|
||||
"hard tasks",
|
||||
"by task name",
|
||||
"by task ID",
|
||||
"by alias",
|
||||
"by tag",
|
||||
"by keyword",
|
||||
"empty result",
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("habitica")
|
||||
async def test_get_tasks(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
service_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test Habitica get_tasks action."""
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_TASKS,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
**service_data,
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert response == snapshot
|
||||
|
Loading…
x
Reference in New Issue
Block a user