diff --git a/homeassistant/components/habitica/const.py b/homeassistant/components/habitica/const.py index ecaa66378f0..049f2beb370 100644 --- a/homeassistant/components/habitica/const.py +++ b/homeassistant/components/habitica/const.py @@ -62,6 +62,7 @@ SERVICE_TRANSFORMATION = "transformation" SERVICE_UPDATE_REWARD = "update_reward" SERVICE_CREATE_REWARD = "create_reward" SERVICE_UPDATE_HABIT = "update_habit" +SERVICE_CREATE_HABIT = "create_habit" DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf" X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}" diff --git a/homeassistant/components/habitica/icons.json b/homeassistant/components/habitica/icons.json index ca4795dd514..af4a20acab6 100644 --- a/homeassistant/components/habitica/icons.json +++ b/homeassistant/components/habitica/icons.json @@ -237,6 +237,12 @@ "tag_options": "mdi:tag", "developer_options": "mdi:test-tube" } + }, + "create_habit": { + "service": "mdi:contrast-box", + "sections": { + "developer_options": "mdi:test-tube" + } } } } diff --git a/homeassistant/components/habitica/services.py b/homeassistant/components/habitica/services.py index 3c4a59990a3..78f3002c89d 100644 --- a/homeassistant/components/habitica/services.py +++ b/homeassistant/components/habitica/services.py @@ -66,6 +66,7 @@ from .const import ( SERVICE_API_CALL, SERVICE_CANCEL_QUEST, SERVICE_CAST_SKILL, + SERVICE_CREATE_HABIT, SERVICE_CREATE_REWARD, SERVICE_GET_TASKS, SERVICE_LEAVE_QUEST, @@ -190,6 +191,7 @@ SERVICE_TASK_TYPE_MAP = { SERVICE_UPDATE_REWARD: TaskType.REWARD, SERVICE_CREATE_REWARD: TaskType.REWARD, SERVICE_UPDATE_HABIT: TaskType.HABIT, + SERVICE_CREATE_HABIT: TaskType.HABIT, } @@ -596,7 +598,7 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 data = Task() if not is_update: - data["type"] = TaskType.REWARD + data["type"] = SERVICE_TASK_TYPE_MAP[call.service] if (text := call.data.get(ATTR_RENAME)) or (text := call.data.get(ATTR_NAME)): data["text"] = text @@ -733,6 +735,13 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 schema=SERVICE_CREATE_TASK_SCHEMA, supports_response=SupportsResponse.ONLY, ) + hass.services.async_register( + DOMAIN, + SERVICE_CREATE_HABIT, + create_or_update_task, + schema=SERVICE_CREATE_TASK_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) hass.services.async_register( DOMAIN, SERVICE_API_CALL, diff --git a/homeassistant/components/habitica/services.yaml b/homeassistant/components/habitica/services.yaml index f5a9c2b0032..ed3ae4516e5 100644 --- a/homeassistant/components/habitica/services.yaml +++ b/homeassistant/components/habitica/services.yaml @@ -183,7 +183,7 @@ update_reward: create_reward: fields: config_entry: *config_entry - name: + name: &name required: true selector: text: @@ -199,7 +199,7 @@ update_habit: task: *task rename: *rename notes: *notes - up_down: + up_down: &up_down required: false selector: select: @@ -210,7 +210,7 @@ update_habit: label: "➖" multiple: true mode: list - priority: + priority: &priority required: false selector: select: @@ -221,7 +221,7 @@ update_habit: - "hard" mode: dropdown translation_key: "priority" - frequency: + frequency: &frequency required: false selector: select: @@ -252,3 +252,13 @@ update_habit: unit_of_measurement: "➖" mode: box alias: *alias +create_habit: + fields: + config_entry: *config_entry + name: *name + notes: *notes + up_down: *up_down + priority: *priority + frequency: *frequency + tag: *tag + developer_options: *developer_options diff --git a/homeassistant/components/habitica/strings.json b/homeassistant/components/habitica/strings.json index 22ea44351da..1f9424eafe1 100644 --- a/homeassistant/components/habitica/strings.json +++ b/homeassistant/components/habitica/strings.json @@ -11,9 +11,9 @@ "config_entry_description": "Select the Habitica account to update a task.", "task_description": "The name (or task ID) of the task you want to update.", "rename_name": "Rename", - "rename_description": "The new title for the Habitica task.", - "notes_name": "Update notes", - "notes_description": "The new notes for the Habitica task.", + "rename_description": "The title for the Habitica task.", + "notes_name": "Notes", + "notes_description": "The notes for the Habitica task.", "tag_name": "Add tags", "tag_description": "Add tags to the Habitica task. If a tag does not already exist, a new one will be created.", "remove_tag_name": "Remove tags", @@ -25,7 +25,13 @@ "tag_options_name": "Tags", "tag_options_description": "Add or remove tags from a task.", "name_description": "The title for the Habitica task.", - "cost_name": "Cost" + "cost_name": "Cost", + "difficulty_name": "Difficulty", + "difficulty_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." }, "config": { "abort": { @@ -793,16 +799,16 @@ "description": "[%key:component::habitica::common::alias_description%]" }, "priority": { - "name": "Difficulty", - "description": "Update the difficulty of a task." + "name": "[%key:component::habitica::common::difficulty_name%]", + "description": "[%key:component::habitica::common::difficulty_description%]" }, "frequency": { - "name": "Counter reset", - "description": "Update when a habit's counter resets: daily resets at the start of a new day, weekly after Sunday night, and monthly at the beginning of a new month." + "name": "[%key:component::habitica::common::frequency_name%]", + "description": "[%key:component::habitica::common::frequency_description%]" }, "up_down": { - "name": "Rewards or losses", - "description": "Update if the habit is good and rewarding (positive), bad and penalizing (negative), or both." + "name": "[%key:component::habitica::common::up_down_name%]", + "description": "[%key:component::habitica::common::up_down_description%]" }, "counter_up": { "name": "Adjust positive counter", @@ -823,6 +829,50 @@ "description": "[%key:component::habitica::common::developer_options_description%]" } } + }, + "create_habit": { + "name": "Create habit", + "description": "Adds a new habit.", + "fields": { + "config_entry": { + "name": "[%key:component::habitica::common::config_entry_name%]", + "description": "Select the Habitica account to create a habit." + }, + "name": { + "name": "[%key:component::habitica::common::task_name%]", + "description": "[%key:component::habitica::common::name_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%]" + }, + "alias": { + "name": "[%key:component::habitica::common::alias_name%]", + "description": "[%key:component::habitica::common::alias_description%]" + }, + "priority": { + "name": "[%key:component::habitica::common::difficulty_name%]", + "description": "[%key:component::habitica::common::difficulty_description%]" + }, + "frequency": { + "name": "[%key:component::habitica::common::frequency_name%]", + "description": "[%key:component::habitica::common::frequency_description%]" + }, + "up_down": { + "name": "[%key:component::habitica::common::up_down_name%]", + "description": "[%key:component::habitica::common::up_down_description%]" + } + }, + "sections": { + "developer_options": { + "name": "[%key:component::habitica::common::developer_options_name%]", + "description": "[%key:component::habitica::common::developer_options_description%]" + } + } } }, "selector": { diff --git a/tests/components/habitica/test_services.py b/tests/components/habitica/test_services.py index 10a8bc0a588..00ad7e6b2e9 100644 --- a/tests/components/habitica/test_services.py +++ b/tests/components/habitica/test_services.py @@ -42,6 +42,7 @@ from homeassistant.components.habitica.const import ( SERVICE_ACCEPT_QUEST, SERVICE_CANCEL_QUEST, SERVICE_CAST_SKILL, + SERVICE_CREATE_HABIT, SERVICE_CREATE_REWARD, SERVICE_GET_TASKS, SERVICE_LEAVE_QUEST, @@ -986,6 +987,10 @@ async def test_update_task_exceptions( ), ], ) +@pytest.mark.parametrize( + "service", + [SERVICE_CREATE_REWARD, SERVICE_CREATE_HABIT], +) @pytest.mark.usefixtures("habitica") async def test_create_task_exceptions( hass: HomeAssistant, @@ -994,6 +999,7 @@ async def test_create_task_exceptions( exception: Exception, expected_exception: Exception, exception_msg: str, + service: str, ) -> None: """Test Habitica task create action exceptions.""" @@ -1001,7 +1007,7 @@ async def test_create_task_exceptions( with pytest.raises(expected_exception, match=exception_msg): await hass.services.async_call( DOMAIN, - SERVICE_CREATE_REWARD, + service, service_data={ ATTR_CONFIG_ENTRY: config_entry.entry_id, ATTR_NAME: "TITLE", @@ -1230,6 +1236,88 @@ async def test_update_habit( habitica.update_task.assert_awaited_with(UUID(task_id), call_args) +@pytest.mark.parametrize( + ("service_data", "call_args"), + [ + ( + { + ATTR_NAME: "TITLE", + }, + Task(type=TaskType.HABIT, text="TITLE"), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_NOTES: "NOTES", + }, + Task(type=TaskType.HABIT, text="TITLE", notes="NOTES"), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_UP_DOWN: [""], + }, + Task(type=TaskType.HABIT, text="TITLE", up=False, down=False), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_UP_DOWN: ["up"], + }, + Task(type=TaskType.HABIT, text="TITLE", up=True, down=False), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_UP_DOWN: ["down"], + }, + Task(type=TaskType.HABIT, text="TITLE", up=False, down=True), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_PRIORITY: "trivial", + }, + Task(type=TaskType.HABIT, text="TITLE", priority=TaskPriority.TRIVIAL), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_FREQUENCY: "daily", + }, + Task(type=TaskType.HABIT, text="TITLE", frequency=Frequency.DAILY), + ), + ( + { + ATTR_NAME: "TITLE", + ATTR_ALIAS: "ALIAS", + }, + Task(type=TaskType.HABIT, text="TITLE", alias="ALIAS"), + ), + ], +) +async def test_create_habit( + hass: HomeAssistant, + config_entry: MockConfigEntry, + habitica: AsyncMock, + service_data: dict[str, Any], + call_args: Task, +) -> None: + """Test Habitica create_habit action.""" + + await hass.services.async_call( + DOMAIN, + SERVICE_CREATE_HABIT, + service_data={ + ATTR_CONFIG_ENTRY: config_entry.entry_id, + **service_data, + }, + return_response=True, + blocking=True, + ) + habitica.create_task.assert_awaited_with(call_args) + + async def test_tags( hass: HomeAssistant, config_entry: MockConfigEntry,