Add update_todo action to Habitica (#139799)

* update_todo action

* fix strings
This commit is contained in:
Manu 2025-03-07 19:40:17 +01:00 committed by GitHub
parent fe34e6beee
commit 3ccb7d80f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 573 additions and 28 deletions

View File

@ -44,6 +44,14 @@ ATTR_UP_DOWN = "up_down"
ATTR_FREQUENCY = "frequency"
ATTR_COUNTER_UP = "counter_up"
ATTR_COUNTER_DOWN = "counter_down"
ATTR_ADD_CHECKLIST_ITEM = "add_checklist_item"
ATTR_REMOVE_CHECKLIST_ITEM = "remove_checklist_item"
ATTR_SCORE_CHECKLIST_ITEM = "score_checklist_item"
ATTR_UNSCORE_CHECKLIST_ITEM = "unscore_checklist_item"
ATTR_REMINDER = "reminder"
ATTR_REMOVE_REMINDER = "remove_reminder"
ATTR_CLEAR_REMINDER = "clear_reminder"
ATTR_CLEAR_DATE = "clear_date"
SERVICE_CAST_SKILL = "cast_skill"
SERVICE_START_QUEST = "start_quest"
@ -63,6 +71,7 @@ SERVICE_UPDATE_REWARD = "update_reward"
SERVICE_CREATE_REWARD = "create_reward"
SERVICE_UPDATE_HABIT = "update_habit"
SERVICE_CREATE_HABIT = "create_habit"
SERVICE_UPDATE_TODO = "update_todo"
DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf"
X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}"

View File

@ -243,6 +243,16 @@
"sections": {
"developer_options": "mdi:test-tube"
}
},
"update_todo": {
"service": "mdi:pencil-box-outline",
"sections": {
"checklist_options": "mdi:format-list-checks",
"tag_options": "mdi:tag",
"developer_options": "mdi:test-tube",
"duedate_options": "mdi:calendar-blank",
"reminder_options": "mdi:reminder"
}
}
}
}

View File

@ -3,17 +3,20 @@
from __future__ import annotations
from dataclasses import asdict
from datetime import datetime, time
import logging
from typing import TYPE_CHECKING, Any, cast
from uuid import UUID
from uuid import UUID, uuid4
from aiohttp import ClientError
from habiticalib import (
Checklist,
Direction,
Frequency,
HabiticaException,
NotAuthorizedError,
NotFoundError,
Reminders,
Skill,
Task,
TaskData,
@ -25,7 +28,7 @@ import voluptuous as vol
from homeassistant.components.todo import ATTR_RENAME
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_NAME, CONF_NAME
from homeassistant.const import ATTR_DATE, ATTR_NAME, CONF_NAME
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@ -38,8 +41,11 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss
from homeassistant.helpers.selector import ConfigEntrySelector
from .const import (
ATTR_ADD_CHECKLIST_ITEM,
ATTR_ALIAS,
ATTR_ARGS,
ATTR_CLEAR_DATE,
ATTR_CLEAR_REMINDER,
ATTR_CONFIG_ENTRY,
ATTR_COST,
ATTR_COUNTER_DOWN,
@ -52,12 +58,17 @@ from .const import (
ATTR_NOTES,
ATTR_PATH,
ATTR_PRIORITY,
ATTR_REMINDER,
ATTR_REMOVE_CHECKLIST_ITEM,
ATTR_REMOVE_REMINDER,
ATTR_REMOVE_TAG,
ATTR_SCORE_CHECKLIST_ITEM,
ATTR_SKILL,
ATTR_TAG,
ATTR_TARGET,
ATTR_TASK,
ATTR_TYPE,
ATTR_UNSCORE_CHECKLIST_ITEM,
ATTR_UP_DOWN,
DOMAIN,
EVENT_API_CALL_SUCCESS,
@ -77,6 +88,7 @@ from .const import (
SERVICE_TRANSFORMATION,
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
)
from .coordinator import HabiticaConfigEntry
@ -137,6 +149,15 @@ BASE_TASK_SCHEMA = vol.Schema(
vol.Optional(ATTR_COUNTER_UP): vol.All(int, vol.Range(0)),
vol.Optional(ATTR_COUNTER_DOWN): vol.All(int, vol.Range(0)),
vol.Optional(ATTR_FREQUENCY): vol.Coerce(Frequency),
vol.Optional(ATTR_DATE): cv.date,
vol.Optional(ATTR_CLEAR_DATE): cv.boolean,
vol.Optional(ATTR_REMINDER): vol.All(cv.ensure_list, [cv.datetime]),
vol.Optional(ATTR_REMOVE_REMINDER): vol.All(cv.ensure_list, [cv.datetime]),
vol.Optional(ATTR_CLEAR_REMINDER): cv.boolean,
vol.Optional(ATTR_ADD_CHECKLIST_ITEM): vol.All(cv.ensure_list, [str]),
vol.Optional(ATTR_REMOVE_CHECKLIST_ITEM): vol.All(cv.ensure_list, [str]),
vol.Optional(ATTR_SCORE_CHECKLIST_ITEM): vol.All(cv.ensure_list, [str]),
vol.Optional(ATTR_UNSCORE_CHECKLIST_ITEM): vol.All(cv.ensure_list, [str]),
}
)
@ -192,6 +213,7 @@ SERVICE_TASK_TYPE_MAP = {
SERVICE_CREATE_REWARD: TaskType.REWARD,
SERVICE_UPDATE_HABIT: TaskType.HABIT,
SERVICE_CREATE_HABIT: TaskType.HABIT,
SERVICE_UPDATE_TODO: TaskType.TODO,
}
@ -577,7 +599,11 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
coordinator = entry.runtime_data
await coordinator.async_refresh()
is_update = call.service in (SERVICE_UPDATE_REWARD, SERVICE_UPDATE_HABIT)
is_update = call.service in (
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
)
current_task = None
if is_update:
@ -685,6 +711,69 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
if counter_down := call.data.get(ATTR_COUNTER_DOWN):
data["counterDown"] = counter_down
if due_date := call.data.get(ATTR_DATE):
data["date"] = datetime.combine(due_date, time())
if call.data.get(ATTR_CLEAR_DATE):
data["date"] = None
checklist = current_task.checklist if current_task else []
if add_checklist_item := call.data.get(ATTR_ADD_CHECKLIST_ITEM):
checklist.extend(
Checklist(completed=False, id=uuid4(), text=item)
for item in add_checklist_item
if not any(i.text == item for i in checklist)
)
if remove_checklist_item := call.data.get(ATTR_REMOVE_CHECKLIST_ITEM):
checklist = [
item for item in checklist if item.text not in remove_checklist_item
]
if score_checklist_item := call.data.get(ATTR_SCORE_CHECKLIST_ITEM):
for item in checklist:
if item.text in score_checklist_item:
item.completed = True
if unscore_checklist_item := call.data.get(ATTR_UNSCORE_CHECKLIST_ITEM):
for item in checklist:
if item.text in unscore_checklist_item:
item.completed = False
if (
add_checklist_item
or remove_checklist_item
or score_checklist_item
or unscore_checklist_item
):
data["checklist"] = checklist
reminders = current_task.reminders if current_task else []
if add_reminders := call.data.get(ATTR_REMINDER):
existing_reminder_datetimes = {
r.time.replace(tzinfo=None) for r in reminders
}
reminders.extend(
Reminders(id=uuid4(), time=r)
for r in add_reminders
if r not in existing_reminder_datetimes
)
if remove_reminder := call.data.get(ATTR_REMOVE_REMINDER):
reminders = list(
filter(
lambda r: r.time.replace(tzinfo=None) not in remove_reminder,
reminders,
)
)
if clear_reminders := call.data.get(ATTR_CLEAR_REMINDER):
reminders = []
if add_reminders or remove_reminder or clear_reminders:
data["reminders"] = reminders
try:
if is_update:
if TYPE_CHECKING:
@ -714,20 +803,14 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
else:
return response.data.to_dict(omit_none=True)
hass.services.async_register(
DOMAIN,
SERVICE_UPDATE_REWARD,
create_or_update_task,
schema=SERVICE_UPDATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_UPDATE_HABIT,
create_or_update_task,
schema=SERVICE_UPDATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
for service in (SERVICE_UPDATE_TODO, SERVICE_UPDATE_REWARD, SERVICE_UPDATE_HABIT):
hass.services.async_register(
DOMAIN,
service,
create_or_update_task,
schema=SERVICE_UPDATE_TASK_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_CREATE_REWARD,

View File

@ -262,3 +262,69 @@ create_habit:
frequency: *frequency
tag: *tag
developer_options: *developer_options
update_todo:
fields:
config_entry: *config_entry
task: *task
rename: *rename
notes: *notes
checklist_options:
collapsed: true
fields:
add_checklist_item:
required: false
selector:
text:
multiple: true
remove_checklist_item:
required: false
selector:
text:
multiple: true
score_checklist_item:
required: false
selector:
text:
multiple: true
unscore_checklist_item:
required: false
selector:
text:
multiple: true
priority: *priority
duedate_options:
collapsed: true
fields:
date:
required: false
selector:
date:
clear_date:
required: false
selector:
constant:
value: true
label: "🗑️"
reminder_options:
collapsed: true
fields:
reminder:
required: false
selector:
text:
type: datetime-local
multiple: true
remove_reminder:
required: false
selector:
text:
type: datetime-local
multiple: true
clear_reminder:
required: false
selector:
constant:
value: true
label: "🗑️"
tag_options: *tag_options
developer_options: *developer_options

View File

@ -26,12 +26,30 @@
"tag_options_description": "Add or remove tags from a task.",
"name_description": "The title for the Habitica task.",
"cost_name": "Cost",
"difficulty_name": "Difficulty",
"difficulty_description": "The difficulty of the task.",
"priority_name": "Difficulty",
"priority_description": "The difficulty of the task.",
"frequency_name": "Counter reset",
"frequency_description": "The frequency at which the habit's counter resets: daily at the start of a new day, weekly after Sunday night, or monthly at the beginning of a new month.",
"up_down_name": "Rewards or losses",
"up_down_description": "Whether the habit is good and rewarding (positive), bad and penalizing (negative), or both."
"up_down_description": "Whether the habit is good and rewarding (positive), bad and penalizing (negative), or both.",
"add_checklist_item_name": "Add checklist items",
"add_checklist_item_description": "The items to add to a task's checklist.",
"remove_checklist_item_name": "Delete items",
"remove_checklist_item_description": "Remove items from a task's checklist.",
"score_checklist_item_name": "Complete items",
"score_checklist_item_description": "Mark items from a task's checklist as completed.",
"unscore_checklist_item_name": "Uncomplete items",
"unscore_checklist_item_description": "Undo completion of items of a task's checklist.",
"checklist_options_name": "Checklist",
"checklist_options_description": "Add, remove, or update status of an item on a task's checklist.",
"reminder_name": "Add reminders",
"reminder_description": "Add reminders to a Habitica task.",
"remove_reminder_name": "Remove reminders",
"remove_reminder_description": "Remove specific reminders from a Habitica task.",
"clear_reminder_name": "Clear all reminders",
"clear_reminder_description": "Remove all reminders from a Habitica task.",
"reminder_options_name": "Reminders",
"reminder_options_description": "Add, remove or clear reminders of a Habitica task."
},
"config": {
"abort": {
@ -659,7 +677,7 @@
"description": "Filter tasks by type."
},
"priority": {
"name": "Difficulty",
"name": "[%key:component::habitica::common::priority_name%]",
"description": "Filter tasks by difficulty."
},
"task": {
@ -799,8 +817,8 @@
"description": "[%key:component::habitica::common::alias_description%]"
},
"priority": {
"name": "[%key:component::habitica::common::difficulty_name%]",
"description": "[%key:component::habitica::common::difficulty_description%]"
"name": "[%key:component::habitica::common::priority_name%]",
"description": "[%key:component::habitica::common::priority_description%]"
},
"frequency": {
"name": "[%key:component::habitica::common::frequency_name%]",
@ -855,8 +873,8 @@
"description": "[%key:component::habitica::common::alias_description%]"
},
"priority": {
"name": "[%key:component::habitica::common::difficulty_name%]",
"description": "[%key:component::habitica::common::difficulty_description%]"
"name": "[%key:component::habitica::common::priority_name%]",
"description": "[%key:component::habitica::common::priority_description%]"
},
"frequency": {
"name": "[%key:component::habitica::common::frequency_name%]",
@ -873,6 +891,102 @@
"description": "[%key:component::habitica::common::developer_options_description%]"
}
}
},
"update_todo": {
"name": "Update a to-do",
"description": "Updates a specific to-do for a selected Habitica character",
"fields": {
"config_entry": {
"name": "[%key:component::habitica::common::config_entry_name%]",
"description": "[%key:component::habitica::common::config_entry_description%]"
},
"task": {
"name": "[%key:component::habitica::common::task_name%]",
"description": "The name (or task ID) of the to-do you want to update."
},
"rename": {
"name": "[%key:component::habitica::common::rename_name%]",
"description": "[%key:component::habitica::common::rename_description%]"
},
"notes": {
"name": "[%key:component::habitica::common::notes_name%]",
"description": "[%key:component::habitica::common::notes_description%]"
},
"tag": {
"name": "[%key:component::habitica::common::tag_name%]",
"description": "[%key:component::habitica::common::tag_description%]"
},
"remove_tag": {
"name": "[%key:component::habitica::common::remove_tag_name%]",
"description": "[%key:component::habitica::common::remove_tag_description%]"
},
"alias": {
"name": "[%key:component::habitica::common::alias_name%]",
"description": "[%key:component::habitica::common::alias_description%]"
},
"priority": {
"name": "[%key:component::habitica::common::priority_name%]",
"description": "[%key:component::habitica::common::priority_description%]"
},
"date": {
"name": "Due date",
"description": "The to-do's due date."
},
"clear_date": {
"name": "Clear due date",
"description": "Remove the due date from the to-do."
},
"reminder": {
"name": "[%key:component::habitica::common::reminder_name%]",
"description": "[%key:component::habitica::common::reminder_description%]"
},
"remove_reminder": {
"name": "[%key:component::habitica::common::remove_reminder_name%]",
"description": "[%key:component::habitica::common::remove_reminder_description%]"
},
"clear_reminder": {
"name": "[%key:component::habitica::common::clear_reminder_name%]",
"description": "[%key:component::habitica::common::clear_reminder_description%]"
},
"add_checklist_item": {
"name": "[%key:component::habitica::common::add_checklist_item_name%]",
"description": "[%key:component::habitica::common::add_checklist_item_description%]"
},
"remove_checklist_item": {
"name": "[%key:component::habitica::common::remove_checklist_item_name%]",
"description": "[%key:component::habitica::common::remove_checklist_item_description%]"
},
"score_checklist_item": {
"name": "[%key:component::habitica::common::score_checklist_item_name%]",
"description": "[%key:component::habitica::common::score_checklist_item_description%]"
},
"unscore_checklist_item": {
"name": "[%key:component::habitica::common::unscore_checklist_item_name%]",
"description": "[%key:component::habitica::common::unscore_checklist_item_description%]"
}
},
"sections": {
"checklist_options": {
"name": "[%key:component::habitica::common::checklist_options_name%]",
"description": "[%key:component::habitica::common::checklist_options_description%]"
},
"duedate_options": {
"name": "Due date",
"description": "Set, update or remove due dates of a to-do."
},
"reminder_options": {
"name": "[%key:component::habitica::common::reminder_options_name%]",
"description": "[%key:component::habitica::common::reminder_options_description%]"
},
"tag_options": {
"name": "[%key:component::habitica::common::tag_options_name%]",
"description": "[%key:component::habitica::common::tag_options_description%]"
},
"developer_options": {
"name": "[%key:component::habitica::common::developer_options_name%]",
"description": "[%key:component::habitica::common::developer_options_description%]"
}
}
}
},
"selector": {

View File

@ -1,7 +1,8 @@
"""Tests for the habitica component."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, MagicMock, patch
from uuid import UUID
from habiticalib import (
BadRequestError,
@ -176,3 +177,13 @@ def mock_setup_entry() -> Generator[AsyncMock]:
"homeassistant.components.habitica.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_uuid4() -> Generator[MagicMock]:
"""Mock uuid4."""
with patch(
"homeassistant.components.habitica.services.uuid4", autospec=True
) as mock_uuid4:
mock_uuid4.return_value = UUID("12345678-1234-5678-1234-567812345678")
yield mock_uuid4

View File

@ -425,7 +425,18 @@
"date": "2024-09-27T22:17:00.000Z",
"completed": false,
"collapseChecklist": false,
"checklist": [],
"checklist": [
{
"completed": false,
"id": "fccc26f2-1e2b-4bf8-9dd0-a405be261036",
"text": "Checklist-item1"
},
{
"completed": true,
"id": "5a897af4-ea94-456a-a2bd-f336bcd79509",
"text": "Checklist-item2"
}
],
"type": "todo",
"text": "Buch zu Ende lesen",
"notes": "Das Buch, das du angefangen hast, bis zum Wochenende fertig lesen.",

View File

@ -736,6 +736,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'fccc26f2-1e2b-4bf8-9dd0-a405be261036',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '5a897af4-ea94-456a-a2bd-f336bcd79509',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -1834,6 +1844,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'fccc26f2-1e2b-4bf8-9dd0-a405be261036',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '5a897af4-ea94-456a-a2bd-f336bcd79509',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -2978,6 +2998,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'fccc26f2-1e2b-4bf8-9dd0-a405be261036',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '5a897af4-ea94-456a-a2bd-f336bcd79509',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -5615,6 +5645,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'fccc26f2-1e2b-4bf8-9dd0-a405be261036',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '5a897af4-ea94-456a-a2bd-f336bcd79509',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -6137,6 +6177,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'fccc26f2-1e2b-4bf8-9dd0-a405be261036',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '5a897af4-ea94-456a-a2bd-f336bcd79509',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,

View File

@ -1,15 +1,18 @@
"""Test Habitica actions."""
from collections.abc import Generator
from datetime import datetime
from typing import Any
from unittest.mock import AsyncMock, patch
from uuid import UUID
from aiohttp import ClientError
from habiticalib import (
Checklist,
Direction,
Frequency,
HabiticaTaskResponse,
Reminders,
Skill,
Task,
TaskPriority,
@ -19,7 +22,10 @@ import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.habitica.const import (
ATTR_ADD_CHECKLIST_ITEM,
ATTR_ALIAS,
ATTR_CLEAR_DATE,
ATTR_CLEAR_REMINDER,
ATTR_CONFIG_ENTRY,
ATTR_COST,
ATTR_COUNTER_DOWN,
@ -30,12 +36,17 @@ from homeassistant.components.habitica.const import (
ATTR_KEYWORD,
ATTR_NOTES,
ATTR_PRIORITY,
ATTR_REMINDER,
ATTR_REMOVE_CHECKLIST_ITEM,
ATTR_REMOVE_REMINDER,
ATTR_REMOVE_TAG,
ATTR_SCORE_CHECKLIST_ITEM,
ATTR_SKILL,
ATTR_TAG,
ATTR_TARGET,
ATTR_TASK,
ATTR_TYPE,
ATTR_UNSCORE_CHECKLIST_ITEM,
ATTR_UP_DOWN,
DOMAIN,
SERVICE_ABORT_QUEST,
@ -53,10 +64,11 @@ from homeassistant.components.habitica.const import (
SERVICE_TRANSFORMATION,
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
)
from homeassistant.components.todo import ATTR_RENAME
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_NAME
from homeassistant.const import ATTR_DATE, ATTR_NAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
@ -938,6 +950,7 @@ async def test_get_tasks(
[
(SERVICE_UPDATE_REWARD, "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"),
(SERVICE_UPDATE_HABIT, "f21fa608-cfc6-4413-9fc7-0eb1b48ca43a"),
(SERVICE_UPDATE_TODO, "88de7cd9-af2b-49ce-9afd-bf941d87336b"),
],
)
@pytest.mark.usefixtures("habitica")
@ -1318,6 +1331,184 @@ async def test_create_habit(
habitica.create_task.assert_awaited_with(call_args)
@pytest.mark.parametrize(
("service_data", "call_args"),
[
(
{
ATTR_RENAME: "RENAME",
},
Task(text="RENAME"),
),
(
{
ATTR_NOTES: "NOTES",
},
Task(notes="NOTES"),
),
(
{
ATTR_ADD_CHECKLIST_ITEM: "Checklist-item",
},
Task(
{
"checklist": [
Checklist(
id=UUID("fccc26f2-1e2b-4bf8-9dd0-a405be261036"),
text="Checklist-item1",
completed=False,
),
Checklist(
id=UUID("5a897af4-ea94-456a-a2bd-f336bcd79509"),
text="Checklist-item2",
completed=True,
),
Checklist(
id=UUID("12345678-1234-5678-1234-567812345678"),
text="Checklist-item",
completed=False,
),
]
}
),
),
(
{
ATTR_REMOVE_CHECKLIST_ITEM: "Checklist-item1",
},
Task(
{
"checklist": [
Checklist(
id=UUID("5a897af4-ea94-456a-a2bd-f336bcd79509"),
text="Checklist-item2",
completed=True,
),
]
}
),
),
(
{
ATTR_SCORE_CHECKLIST_ITEM: "Checklist-item1",
},
Task(
{
"checklist": [
Checklist(
id=UUID("fccc26f2-1e2b-4bf8-9dd0-a405be261036"),
text="Checklist-item1",
completed=True,
),
Checklist(
id=UUID("5a897af4-ea94-456a-a2bd-f336bcd79509"),
text="Checklist-item2",
completed=True,
),
]
}
),
),
(
{
ATTR_UNSCORE_CHECKLIST_ITEM: "Checklist-item2",
},
Task(
{
"checklist": [
Checklist(
id=UUID("fccc26f2-1e2b-4bf8-9dd0-a405be261036"),
text="Checklist-item1",
completed=False,
),
Checklist(
id=UUID("5a897af4-ea94-456a-a2bd-f336bcd79509"),
text="Checklist-item2",
completed=False,
),
]
}
),
),
(
{
ATTR_PRIORITY: "trivial",
},
Task(priority=TaskPriority.TRIVIAL),
),
(
{
ATTR_DATE: "2025-03-05",
},
Task(date=datetime(2025, 3, 5)),
),
(
{
ATTR_CLEAR_DATE: True,
},
Task(date=None),
),
(
{
ATTR_REMINDER: ["2025-02-25T00:00"],
},
Task(
{
"reminders": [
Reminders(
id=UUID("12345678-1234-5678-1234-567812345678"),
time=datetime(2025, 2, 25, 0, 0),
startDate=None,
)
]
}
),
),
(
{
ATTR_REMOVE_REMINDER: ["2025-02-25T00:00"],
},
Task({"reminders": []}),
),
(
{
ATTR_CLEAR_REMINDER: True,
},
Task({"reminders": []}),
),
(
{
ATTR_ALIAS: "ALIAS",
},
Task(alias="ALIAS"),
),
],
)
@pytest.mark.usefixtures("mock_uuid4")
async def test_update_todo(
hass: HomeAssistant,
config_entry: MockConfigEntry,
habitica: AsyncMock,
service_data: dict[str, Any],
call_args: Task,
) -> None:
"""Test Habitica update todo action."""
task_id = "88de7cd9-af2b-49ce-9afd-bf941d87336b"
await hass.services.async_call(
DOMAIN,
SERVICE_UPDATE_TODO,
service_data={
ATTR_CONFIG_ENTRY: config_entry.entry_id,
ATTR_TASK: task_id,
**service_data,
},
return_response=True,
blocking=True,
)
habitica.update_task.assert_awaited_with(UUID(task_id), call_args)
async def test_tags(
hass: HomeAssistant,
config_entry: MockConfigEntry,