Add update_daily action to Habitica integration (#140328)

* add update_daily action

* day strings
This commit is contained in:
Manu 2025-03-14 16:49:56 +01:00 committed by GitHub
parent 324f208d68
commit 78a04776e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 691 additions and 24 deletions

View File

@ -52,6 +52,11 @@ ATTR_REMINDER = "reminder"
ATTR_REMOVE_REMINDER = "remove_reminder"
ATTR_CLEAR_REMINDER = "clear_reminder"
ATTR_CLEAR_DATE = "clear_date"
ATTR_REPEAT = "repeat"
ATTR_INTERVAL = "every_x"
ATTR_START_DATE = "start_date"
ATTR_REPEAT_MONTHLY = "repeat_monthly"
ATTR_STREAK = "streak"
SERVICE_CAST_SKILL = "cast_skill"
SERVICE_START_QUEST = "start_quest"
@ -73,6 +78,7 @@ SERVICE_UPDATE_HABIT = "update_habit"
SERVICE_CREATE_HABIT = "create_habit"
SERVICE_UPDATE_TODO = "update_todo"
SERVICE_CREATE_TODO = "create_todo"
SERVICE_UPDATE_DAILY = "update_daily"
DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf"
X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}"
@ -80,3 +86,5 @@ X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}"
SECTION_REAUTH_LOGIN = "reauth_login"
SECTION_REAUTH_API_KEY = "reauth_api_key"
SECTION_DANGER_ZONE = "danger_zone"
WEEK_DAYS = ["m", "t", "w", "th", "f", "s", "su"]

View File

@ -259,6 +259,17 @@
"sections": {
"developer_options": "mdi:test-tube"
}
},
"update_daily": {
"service": "mdi:calendar-month",
"sections": {
"checklist_options": "mdi:format-list-checks",
"tag_options": "mdi:tag",
"developer_options": "mdi:test-tube",
"reminder_options": "mdi:reminder",
"repeat_weekly_options": "mdi:calendar-refresh",
"repeat_monthly_options": "mdi:calendar-refresh"
}
}
}
}

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import asdict
from datetime import datetime, time
from datetime import UTC, date, datetime, time
import logging
from typing import TYPE_CHECKING, Any, cast
from uuid import UUID, uuid4
@ -17,6 +17,7 @@ from habiticalib import (
NotAuthorizedError,
NotFoundError,
Reminders,
Repeat,
Skill,
Task,
TaskData,
@ -39,6 +40,7 @@ from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.selector import ConfigEntrySelector
from homeassistant.util import dt as dt_util
from .const import (
ATTR_ADD_CHECKLIST_ITEM,
@ -53,6 +55,7 @@ from .const import (
ATTR_DATA,
ATTR_DIRECTION,
ATTR_FREQUENCY,
ATTR_INTERVAL,
ATTR_ITEM,
ATTR_KEYWORD,
ATTR_NOTES,
@ -62,8 +65,12 @@ from .const import (
ATTR_REMOVE_CHECKLIST_ITEM,
ATTR_REMOVE_REMINDER,
ATTR_REMOVE_TAG,
ATTR_REPEAT,
ATTR_REPEAT_MONTHLY,
ATTR_SCORE_CHECKLIST_ITEM,
ATTR_SKILL,
ATTR_START_DATE,
ATTR_STREAK,
ATTR_TAG,
ATTR_TARGET,
ATTR_TASK,
@ -87,9 +94,11 @@ from .const import (
SERVICE_SCORE_REWARD,
SERVICE_START_QUEST,
SERVICE_TRANSFORMATION,
SERVICE_UPDATE_DAILY,
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
WEEK_DAYS,
)
from .coordinator import HabiticaConfigEntry
@ -152,13 +161,24 @@ BASE_TASK_SCHEMA = vol.Schema(
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_REMINDER): vol.All(
cv.ensure_list, [vol.Any(cv.datetime, cv.time)]
),
vol.Optional(ATTR_REMOVE_REMINDER): vol.All(
cv.ensure_list, [vol.Any(cv.datetime, cv.time)]
),
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]),
vol.Optional(ATTR_START_DATE): cv.date,
vol.Optional(ATTR_INTERVAL): vol.All(int, vol.Range(0)),
vol.Optional(ATTR_REPEAT): vol.All(cv.ensure_list, [vol.In(WEEK_DAYS)]),
vol.Optional(ATTR_REPEAT_MONTHLY): vol.All(
cv.string, vol.In({"day_of_month", "day_of_week"})
),
vol.Optional(ATTR_STREAK): vol.All(int, vol.Range(0)),
}
)
@ -175,6 +195,12 @@ SERVICE_CREATE_TASK_SCHEMA = BASE_TASK_SCHEMA.extend(
}
)
SERVICE_DAILY_SCHEMA = {
vol.Optional(ATTR_REMINDER): vol.All(cv.ensure_list, [cv.time]),
vol.Optional(ATTR_REMOVE_REMINDER): vol.All(cv.ensure_list, [cv.time]),
}
SERVICE_GET_TASKS_SCHEMA = vol.Schema(
{
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector({"integration": DOMAIN}),
@ -216,6 +242,7 @@ SERVICE_TASK_TYPE_MAP = {
SERVICE_CREATE_HABIT: TaskType.HABIT,
SERVICE_UPDATE_TODO: TaskType.TODO,
SERVICE_CREATE_TODO: TaskType.TODO,
SERVICE_UPDATE_DAILY: TaskType.DAILY,
}
@ -605,7 +632,9 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
SERVICE_UPDATE_DAILY,
)
task_type = SERVICE_TASK_TYPE_MAP[call.service]
current_task = None
if is_update:
@ -614,7 +643,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
task
for task in coordinator.data.tasks
if call.data[ATTR_TASK] in (str(task.id), task.alias, task.text)
and task.Type is SERVICE_TASK_TYPE_MAP[call.service]
and task.Type is task_type
)
except StopIteration as e:
raise ServiceValidationError(
@ -626,7 +655,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
data = Task()
if not is_update:
data["type"] = SERVICE_TASK_TYPE_MAP[call.service]
data["type"] = task_type
if (text := call.data.get(ATTR_RENAME)) or (text := call.data.get(ATTR_NAME)):
data["text"] = text
@ -702,6 +731,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
if frequency := call.data.get(ATTR_FREQUENCY):
data["frequency"] = frequency
else:
frequency = current_task.frequency if current_task else Frequency.WEEKLY
if up_down := call.data.get(ATTR_UP_DOWN):
data["up"] = "up" in up_down
@ -752,23 +783,46 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
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
}
if task_type is TaskType.TODO:
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
)
reminders.extend(
Reminders(id=uuid4(), time=r)
for r in add_reminders
if r not in existing_reminder_datetimes
)
if task_type is TaskType.DAILY:
existing_reminder_times = {
r.time.time().replace(microsecond=0, second=0) for r in reminders
}
reminders.extend(
Reminders(
id=uuid4(),
time=datetime.combine(date.today(), r, tzinfo=UTC),
)
for r in add_reminders
if r not in existing_reminder_times
)
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 task_type is TaskType.TODO:
reminders = list(
filter(
lambda r: r.time.replace(tzinfo=None) not in remove_reminder,
reminders,
)
)
if task_type is TaskType.DAILY:
reminders = list(
filter(
lambda r: r.time.time().replace(second=0, microsecond=0)
not in remove_reminder,
reminders,
)
)
)
if clear_reminders := call.data.get(ATTR_CLEAR_REMINDER):
reminders = []
@ -776,6 +830,47 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
if add_reminders or remove_reminder or clear_reminders:
data["reminders"] = reminders
if start_date := call.data.get(ATTR_START_DATE):
data["startDate"] = datetime.combine(start_date, time())
else:
start_date = (
current_task.startDate
if current_task and current_task.startDate
else dt_util.start_of_local_day()
)
if repeat := call.data.get(ATTR_REPEAT):
if frequency is Frequency.WEEKLY:
data["repeat"] = Repeat(**{d: d in repeat for d in WEEK_DAYS})
else:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="frequency_not_weekly",
)
if repeat_monthly := call.data.get(ATTR_REPEAT_MONTHLY):
if frequency is not Frequency.MONTHLY:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="frequency_not_monthly",
)
if repeat_monthly == "day_of_week":
weekday = start_date.weekday()
data["weeksOfMonth"] = [(start_date.day - 1) // 7]
data["repeat"] = Repeat(
**{day: i == weekday for i, day in enumerate(WEEK_DAYS)}
)
data["daysOfMonth"] = []
else:
data["daysOfMonth"] = [start_date.day]
data["weeksOfMonth"] = []
if interval := call.data.get(ATTR_INTERVAL):
data["everyX"] = interval
if streak := call.data.get(ATTR_STREAK):
data["streak"] = streak
try:
if is_update:
if TYPE_CHECKING:
@ -805,7 +900,12 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
else:
return response.data.to_dict(omit_none=True)
for service in (SERVICE_UPDATE_TODO, SERVICE_UPDATE_REWARD, SERVICE_UPDATE_HABIT):
for service in (
SERVICE_UPDATE_DAILY,
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
):
hass.services.async_register(
DOMAIN,
service,

View File

@ -268,7 +268,7 @@ update_todo:
task: *task
rename: *rename
notes: *notes
checklist_options:
checklist_options: &checklist_options
collapsed: true
fields:
add_checklist_item: &add_checklist_item
@ -320,7 +320,7 @@ update_todo:
text:
type: datetime-local
multiple: true
clear_reminder:
clear_reminder: &clear_reminder
required: false
selector:
constant:
@ -339,3 +339,93 @@ create_todo:
reminder: *reminder
tag: *tag
developer_options: *developer_options
update_daily:
fields:
config_entry: *config_entry
task: *task
rename: *rename
notes: *notes
checklist_options: *checklist_options
priority: *priority
start_date:
required: false
selector:
date:
frequency:
required: false
selector:
select:
options:
- "daily"
- "weekly"
- "monthly"
- "yearly"
translation_key: "frequency"
mode: dropdown
every_x:
required: false
selector:
number:
min: 0
step: 1
unit_of_measurement: "🔃"
mode: box
repeat_weekly_options:
collapsed: true
fields:
repeat:
required: false
selector:
select:
options:
- "m"
- "t"
- "w"
- "th"
- "f"
- "s"
- "su"
mode: list
translation_key: repeat
multiple: true
repeat_monthly_options:
collapsed: true
fields:
repeat_monthly:
required: false
selector:
select:
options:
- "day_of_month"
- "day_of_week"
translation_key: repeat_monthly
mode: list
reminder_options:
collapsed: true
fields:
reminder:
required: false
selector:
text:
type: time
multiple: true
remove_reminder:
required: false
selector:
text:
type: time
multiple: true
clear_reminder: *clear_reminder
tag_options: *tag_options
developer_options:
collapsed: true
fields:
streak:
required: false
selector:
number:
min: 0
step: 1
unit_of_measurement: "▶▶"
mode: box
alias: *alias

View File

@ -51,7 +51,8 @@
"reminder_options_name": "Reminders",
"reminder_options_description": "Add, remove or clear reminders of a Habitica task.",
"date_name": "Due date",
"date_description": "The to-do's due date."
"date_description": "The to-do's due date.",
"repeat_name": "Repeat on"
},
"config": {
"abort": {
@ -1037,6 +1038,122 @@
"description": "[%key:component::habitica::common::developer_options_description%]"
}
}
},
"update_daily": {
"name": "Update a daily",
"description": "Updates a specific daily 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 daily 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%]"
},
"start_date": {
"name": "Start date",
"description": "Defines when the daily task becomes active and specifies the exact weekday or day of the month it repeats on."
},
"frequency": {
"name": "Repeat interval",
"description": "The repetition interval of a daily."
},
"every_x": {
"name": "Repeat every X",
"description": "The number of intervals (days, weeks, months, or years) after which the daily repeats, based on the chosen repetition interval. A value of 0 makes the daily inactive ('Grey Daily')."
},
"repeat": {
"name": "[%key:component::habitica::common::repeat_name%]",
"description": "The days of the week the daily repeats."
},
"repeat_monthly": {
"name": "[%key:component::habitica::common::repeat_name%]",
"description": "Whether a monthly recurring task repeats on the same calendar day each month or on the same weekday and week of the month, based on the start date."
},
"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%]"
},
"streak": {
"name": "Adjust streak",
"description": "Adjust or reset the streak counter of the daily."
},
"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%]"
}
},
"sections": {
"checklist_options": {
"name": "[%key:component::habitica::common::checklist_options_name%]",
"description": "[%key:component::habitica::common::checklist_options_description%]"
},
"repeat_weekly_options": {
"name": "Weekly repeat days",
"description": "Options related to weekly repetition, applicable when the repetition interval is set to weekly."
},
"repeat_monthly_options": {
"name": "Monthly repeat day",
"description": "Options related to monthly repetition, applicable when the repetition interval is set to monthly."
},
"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%]"
},
"reminder_options": {
"name": "[%key:component::habitica::common::reminder_options_name%]",
"description": "[%key:component::habitica::common::reminder_options_description%]"
}
}
}
},
"selector": {
@ -1079,6 +1196,23 @@
"monthly": "Monthly",
"yearly": "Yearly"
}
},
"repeat": {
"options": {
"m": "[%key:common::time::monday%]",
"t": "[%key:common::time::tuesday%]",
"w": "[%key:common::time::wednesday%]",
"th": "[%key:common::time::thursday%]",
"f": "[%key:common::time::friday%]",
"s": "[%key:common::time::saturday%]",
"su": "[%key:common::time::sunday%]"
}
},
"repeat_monthly": {
"options": {
"day_of_month": "Day of the month",
"day_of_week": "Day of the week"
}
}
}
}

View File

@ -605,7 +605,18 @@
"startDate": "2024-09-20T23:00:00.000Z",
"daysOfMonth": [],
"weeksOfMonth": [3],
"checklist": [],
"checklist": [
{
"completed": false,
"id": "a2a6702d-58e1-46c2-a3ce-422d525cc0b6",
"text": "Checklist-item1"
},
{
"completed": true,
"id": "9f64e1cd-b0ab-4577-8344-c7a5e1827997",
"text": "Checklist-item2"
}
],
"reminders": [],
"createdAt": "2024-10-10T15:57:14.304Z",
"updatedAt": "2024-11-27T23:47:29.986Z",

View File

@ -1116,6 +1116,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'a2a6702d-58e1-46c2-a3ce-422d525cc0b6',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '9f64e1cd-b0ab-4577-8344-c7a5e1827997',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -3378,6 +3388,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'a2a6702d-58e1-46c2-a3ce-422d525cc0b6',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '9f64e1cd-b0ab-4577-8344-c7a5e1827997',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -4511,6 +4531,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'a2a6702d-58e1-46c2-a3ce-422d525cc0b6',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '9f64e1cd-b0ab-4577-8344-c7a5e1827997',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,
@ -5092,6 +5122,16 @@
'winner': None,
}),
'checklist': list([
dict({
'completed': False,
'id': 'a2a6702d-58e1-46c2-a3ce-422d525cc0b6',
'text': 'Checklist-item1',
}),
dict({
'completed': True,
'id': '9f64e1cd-b0ab-4577-8344-c7a5e1827997',
'text': 'Checklist-item2',
}),
]),
'collapseChecklist': False,
'completed': False,

View File

@ -1,18 +1,20 @@
"""Test Habitica actions."""
from collections.abc import Generator
from datetime import datetime
from datetime import UTC, datetime
from typing import Any
from unittest.mock import AsyncMock, patch
from uuid import UUID
from aiohttp import ClientError
from freezegun.api import freeze_time
from habiticalib import (
Checklist,
Direction,
Frequency,
HabiticaTaskResponse,
Reminders,
Repeat,
Skill,
Task,
TaskPriority,
@ -32,6 +34,7 @@ from homeassistant.components.habitica.const import (
ATTR_COUNTER_UP,
ATTR_DIRECTION,
ATTR_FREQUENCY,
ATTR_INTERVAL,
ATTR_ITEM,
ATTR_KEYWORD,
ATTR_NOTES,
@ -40,8 +43,12 @@ from homeassistant.components.habitica.const import (
ATTR_REMOVE_CHECKLIST_ITEM,
ATTR_REMOVE_REMINDER,
ATTR_REMOVE_TAG,
ATTR_REPEAT,
ATTR_REPEAT_MONTHLY,
ATTR_SCORE_CHECKLIST_ITEM,
ATTR_SKILL,
ATTR_START_DATE,
ATTR_STREAK,
ATTR_TAG,
ATTR_TARGET,
ATTR_TASK,
@ -63,6 +70,7 @@ from homeassistant.components.habitica.const import (
SERVICE_SCORE_REWARD,
SERVICE_START_QUEST,
SERVICE_TRANSFORMATION,
SERVICE_UPDATE_DAILY,
SERVICE_UPDATE_HABIT,
SERVICE_UPDATE_REWARD,
SERVICE_UPDATE_TODO,
@ -952,6 +960,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"),
(SERVICE_UPDATE_DAILY, "6e53f1f5-a315-4edd-984d-8d762e4a08ef"),
],
)
@pytest.mark.usefixtures("habitica")
@ -1606,6 +1615,270 @@ async def test_create_todo(
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("a2a6702d-58e1-46c2-a3ce-422d525cc0b6"),
text="Checklist-item1",
completed=False,
),
Checklist(
id=UUID("9f64e1cd-b0ab-4577-8344-c7a5e1827997"),
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("9f64e1cd-b0ab-4577-8344-c7a5e1827997"),
text="Checklist-item2",
completed=True,
),
]
}
),
),
(
{
ATTR_SCORE_CHECKLIST_ITEM: "Checklist-item1",
},
Task(
{
"checklist": [
Checklist(
id=UUID("a2a6702d-58e1-46c2-a3ce-422d525cc0b6"),
text="Checklist-item1",
completed=True,
),
Checklist(
id=UUID("9f64e1cd-b0ab-4577-8344-c7a5e1827997"),
text="Checklist-item2",
completed=True,
),
]
}
),
),
(
{
ATTR_UNSCORE_CHECKLIST_ITEM: "Checklist-item2",
},
Task(
{
"checklist": [
Checklist(
id=UUID("a2a6702d-58e1-46c2-a3ce-422d525cc0b6"),
text="Checklist-item1",
completed=False,
),
Checklist(
id=UUID("9f64e1cd-b0ab-4577-8344-c7a5e1827997"),
text="Checklist-item2",
completed=False,
),
]
}
),
),
(
{
ATTR_PRIORITY: "trivial",
},
Task(priority=TaskPriority.TRIVIAL),
),
(
{
ATTR_START_DATE: "2025-03-05",
},
Task(startDate=datetime(2025, 3, 5)),
),
(
{
ATTR_FREQUENCY: "weekly",
},
Task(frequency=Frequency.WEEKLY),
),
(
{
ATTR_INTERVAL: 5,
},
Task(everyX=5),
),
(
{
ATTR_FREQUENCY: "weekly",
ATTR_REPEAT: ["m", "t", "w", "th"],
},
Task(
frequency=Frequency.WEEKLY,
repeat=Repeat(m=True, t=True, w=True, th=True),
),
),
(
{
ATTR_FREQUENCY: "monthly",
ATTR_REPEAT_MONTHLY: "day_of_month",
},
Task(frequency=Frequency.MONTHLY, daysOfMonth=[20], weeksOfMonth=[]),
),
(
{
ATTR_FREQUENCY: "monthly",
ATTR_REPEAT_MONTHLY: "day_of_week",
},
Task(
frequency=Frequency.MONTHLY,
daysOfMonth=[],
weeksOfMonth=[2],
repeat=Repeat(
m=False, t=False, w=False, th=False, f=True, s=False, su=False
),
),
),
(
{
ATTR_REMINDER: ["10:00"],
},
Task(
{
"reminders": [
Reminders(
id=UUID("12345678-1234-5678-1234-567812345678"),
time=datetime(2025, 2, 25, 10, 0, tzinfo=UTC),
startDate=None,
)
]
}
),
),
(
{
ATTR_REMOVE_REMINDER: ["10:00"],
},
Task({"reminders": []}),
),
(
{
ATTR_CLEAR_REMINDER: True,
},
Task({"reminders": []}),
),
(
{
ATTR_STREAK: 10,
},
Task(streak=10),
),
(
{
ATTR_ALIAS: "ALIAS",
},
Task(alias="ALIAS"),
),
],
)
@pytest.mark.usefixtures("mock_uuid4")
@freeze_time("2025-02-25T22:00:00.000Z")
async def test_update_daily(
hass: HomeAssistant,
config_entry: MockConfigEntry,
habitica: AsyncMock,
service_data: dict[str, Any],
call_args: Task,
) -> None:
"""Test Habitica update daily action."""
task_id = "6e53f1f5-a315-4edd-984d-8d762e4a08ef"
await hass.services.async_call(
DOMAIN,
SERVICE_UPDATE_DAILY,
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)
@pytest.mark.parametrize(
"service_data",
[
{
ATTR_FREQUENCY: "daily",
ATTR_REPEAT: ["m", "t", "w", "th"],
},
{
ATTR_FREQUENCY: "weekly",
ATTR_REPEAT_MONTHLY: "day_of_month",
},
{
ATTR_FREQUENCY: "weekly",
ATTR_REPEAT_MONTHLY: "day_of_week",
},
],
)
@pytest.mark.usefixtures("mock_uuid4")
@freeze_time("2025-02-25T22:00:00.000Z")
async def test_update_daily_service_validation_errors(
hass: HomeAssistant,
config_entry: MockConfigEntry,
habitica: AsyncMock,
service_data: dict[str, Any],
) -> None:
"""Test Habitica update daily action."""
task_id = "6e53f1f5-a315-4edd-984d-8d762e4a08ef"
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
DOMAIN,
SERVICE_UPDATE_DAILY,
service_data={
ATTR_CONFIG_ENTRY: config_entry.entry_id,
ATTR_TASK: task_id,
**service_data,
},
return_response=True,
blocking=True,
)
async def test_tags(
hass: HomeAssistant,
config_entry: MockConfigEntry,