Skip to content

Commit

Permalink
Add get_tasks action to Habitica integration (home-assistant#127687)
Browse files Browse the repository at this point in the history
Add get_tasks action
  • Loading branch information
tr4nt0r authored Jan 3, 2025
1 parent add401f commit 5726d09
Show file tree
Hide file tree
Showing 10 changed files with 6,545 additions and 8 deletions.
7 changes: 7 additions & 0 deletions homeassistant/components/habitica/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@
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"
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"

Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/habitica/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@
},
"transformation": {
"service": "mdi:flask-round-bottom"
},
"get_tasks": {
"service": "mdi:calendar-export",
"sections": {
"filter": "mdi:calendar-filter"
}
}
}
}
78 changes: 77 additions & 1 deletion homeassistant/components/habitica/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from dataclasses import asdict
import logging
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from aiohttp import ClientError
from habiticalib import (
Expand All @@ -13,6 +13,9 @@
NotAuthorizedError,
NotFoundError,
Skill,
TaskData,
TaskPriority,
TaskType,
TooManyRequestsError,
)
import voluptuous as vol
Expand All @@ -36,17 +39,22 @@
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,
SERVICE_ACCEPT_QUEST,
SERVICE_API_CALL,
SERVICE_CANCEL_QUEST,
SERVICE_CAST_SKILL,
SERVICE_GET_TASKS,
SERVICE_LEAVE_QUEST,
SERVICE_REJECT_QUEST,
SERVICE_SCORE_HABIT,
Expand Down Expand Up @@ -96,6 +104,21 @@
}
)

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,
Expand Down Expand Up @@ -403,6 +426,52 @@ async def transformation(call: ServiceCall) -> ServiceResponse:
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,
Expand Down Expand Up @@ -440,3 +509,10 @@ async def transformation(call: ServiceCall) -> ServiceResponse:
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,
)
46 changes: 46 additions & 0 deletions homeassistant/components/habitica/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
55 changes: 54 additions & 1 deletion homeassistant/components/habitica/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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."
}
}
Expand Down Expand Up @@ -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": {
Expand All @@ -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"
}
}
}
}
26 changes: 21 additions & 5 deletions tests/components/habitica/fixtures/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
30 changes: 30 additions & 0 deletions tests/components/habitica/fixtures/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 5726d09

Please sign in to comment.