Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor todo services and their schema #103079

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 41 additions & 51 deletions homeassistant/components/todo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,45 +43,38 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
websocket_api.async_register_command(hass, websocket_handle_todo_item_move)

component.async_register_entity_service(
"create_item",
"add_item",
{
vol.Required("summary"): vol.All(cv.string, vol.Length(min=1)),
vol.Optional("status", default=TodoItemStatus.NEEDS_ACTION): vol.In(
{TodoItemStatus.NEEDS_ACTION, TodoItemStatus.COMPLETED}
),
vol.Required("item"): vol.All(cv.string, vol.Length(min=1)),
},
_async_create_todo_item,
_async_add_todo_item,
required_features=[TodoListEntityFeature.CREATE_TODO_ITEM],
)
component.async_register_entity_service(
"update_item",
vol.All(
cv.make_entity_service_schema(
{
vol.Optional("uid"): cv.string,
vol.Optional("summary"): vol.All(cv.string, vol.Length(min=1)),
vol.Required("item"): vol.All(cv.string, vol.Length(min=1)),
vol.Optional("rename"): vol.All(cv.string, vol.Length(min=1)),
vol.Optional("status"): vol.In(
{TodoItemStatus.NEEDS_ACTION, TodoItemStatus.COMPLETED}
),
}
),
cv.has_at_least_one_key("uid", "summary"),
cv.has_at_least_one_key("rename", "status"),
),
_async_update_todo_item,
required_features=[TodoListEntityFeature.UPDATE_TODO_ITEM],
)
component.async_register_entity_service(
"delete_item",
vol.All(
cv.make_entity_service_schema(
{
vol.Optional("uid"): vol.All(cv.ensure_list, [cv.string]),
vol.Optional("summary"): vol.All(cv.ensure_list, [cv.string]),
}
),
cv.has_at_least_one_key("uid", "summary"),
"remove_item",
cv.make_entity_service_schema(
{
vol.Required("item"): vol.All(cv.ensure_list, [cv.string]),
}
),
_async_delete_todo_items,
_async_remove_todo_items,
required_features=[TodoListEntityFeature.DELETE_TODO_ITEM],
)

Expand Down Expand Up @@ -114,13 +107,6 @@ class TodoItem:
status: TodoItemStatus | None = None
"""A status or confirmation of the To-do item."""

@classmethod
def from_dict(cls, obj: dict[str, Any]) -> "TodoItem":
"""Create a To-do Item from a dictionary parsed by schema validators."""
return cls(
summary=obj.get("summary"), status=obj.get("status"), uid=obj.get("uid")
)


class TodoListEntity(Entity):
"""An entity that represents a To-do list."""
Expand Down Expand Up @@ -232,39 +218,43 @@ async def websocket_handle_todo_item_move(
connection.send_result(msg["id"])


def _find_by_summary(summary: str, items: list[TodoItem] | None) -> TodoItem | None:
"""Find a To-do List item by summary name."""
def _find_by_uid_or_summary(
value: str, items: list[TodoItem] | None
) -> TodoItem | None:
"""Find a To-do List item by uid or summary name."""
for item in items or ():
if item.summary == summary:
if value in (item.uid, item.summary):
return item
return None


async def _async_create_todo_item(entity: TodoListEntity, call: ServiceCall) -> None:
async def _async_add_todo_item(entity: TodoListEntity, call: ServiceCall) -> None:
"""Add an item to the To-do list."""
await entity.async_create_todo_item(item=TodoItem.from_dict(call.data))
await entity.async_create_todo_item(
item=TodoItem(summary=call.data["item"], status=TodoItemStatus.NEEDS_ACTION)
)


async def _async_update_todo_item(entity: TodoListEntity, call: ServiceCall) -> None:
"""Update an item in the To-do list."""
item = TodoItem.from_dict(call.data)
if not item.uid:
found = _find_by_summary(call.data["summary"], entity.todo_items)
if not found:
raise ValueError(f"Unable to find To-do item with summary '{item.summary}'")
item.uid = found.uid

await entity.async_update_todo_item(item=item)


async def _async_delete_todo_items(entity: TodoListEntity, call: ServiceCall) -> None:
"""Delete an item in the To-do list."""
uids = call.data.get("uid", [])
if not uids:
summaries = call.data.get("summary", [])
for summary in summaries:
item = _find_by_summary(summary, entity.todo_items)
if not item:
raise ValueError(f"Unable to find To-do item with summary '{summary}")
uids.append(item.uid)
item = call.data["item"]
found = _find_by_uid_or_summary(item, entity.todo_items)
if not found:
raise ValueError(f"Unable to find To-do item '{item}'")

update_item = TodoItem(
uid=found.uid, summary=call.data.get("rename"), status=call.data.get("status")
)

await entity.async_update_todo_item(item=update_item)


async def _async_remove_todo_items(entity: TodoListEntity, call: ServiceCall) -> None:
"""Remove an item in the To-do list."""
uids = []
for item in call.data.get("item", []):
found = _find_by_uid_or_summary(item, entity.todo_items)
if not found or not found.uid:
raise ValueError(f"Unable to find To-do item '{item}")
uids.append(found.uid)
await entity.async_delete_todo_items(uids=uids)
27 changes: 9 additions & 18 deletions homeassistant/components/todo/services.yaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
create_item:
add_item:
target:
entity:
domain: todo
supported_features:
- todo.TodoListEntityFeature.CREATE_TODO_ITEM
fields:
summary:
item:
required: true
example: "Submit income tax return"
selector:
text:
status:
example: "needs_action"
selector:
select:
translation_key: status
options:
- needs_action
- completed
update_item:
target:
entity:
domain: todo
supported_features:
- todo.TodoListEntityFeature.UPDATE_TODO_ITEM
fields:
uid:
item:
required: true
example: "Submit income tax return"
selector:
text:
summary:
example: "Submit income tax return"
rename:
example: "Something else"
selector:
text:
status:
Expand All @@ -40,16 +34,13 @@ update_item:
options:
- needs_action
- completed
delete_item:
remove_item:
target:
entity:
domain: todo
supported_features:
- todo.TodoListEntityFeature.DELETE_TODO_ITEM
fields:
uid:
selector:
object:
summary:
item:
selector:
object:
46 changes: 19 additions & 27 deletions homeassistant/components/todo/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,41 @@
}
},
"services": {
"create_item": {
"name": "Create to-do list item",
"add_item": {
"name": "Add to-do list item",
"description": "Add a new to-do list item.",
"fields": {
"summary": {
"name": "Summary",
"description": "The short summary that represents the to-do item."
},
"status": {
"name": "Status",
"description": "A status or confirmation of the to-do item."
"item": {
"name": "Item name",
"description": "The name that represents the to-do item."
}
}
},
"update_item": {
"name": "Update to-do list item",
"description": "Update an existing to-do list item based on either its unique ID or summary.",
"description": "Update an existing to-do list item based on its name.",
"fields": {
"uid": {
"name": "To-do item unique ID",
"description": "Unique identifier for the to-do list item."
"item": {
"name": "Item name",
"description": "The name for the to-do list item."
},
"summary": {
"name": "Summary",
"description": "The short summary that represents the to-do item."
"rename": {
"name": "Rename item",
"description": "The new name of the to-do item"
},
"status": {
"name": "Status",
"name": "Set status",
"description": "A status or confirmation of the to-do item."
}
}
},
"delete_item": {
"name": "Delete a to-do list item",
"description": "Delete an existing to-do list item either by its unique ID or summary.",
"remove_item": {
"name": "Remove a to-do list item",
"description": "Remove an existing to-do list item by its name.",
"fields": {
"uid": {
"name": "To-do item unique IDs",
"description": "Unique identifiers for the to-do list items."
},
"summary": {
"name": "Summary",
"description": "The short summary that represents the to-do item."
"item": {
"name": "Item name",
"description": "The name for the to-do list items."
}
}
}
Expand Down
Loading