Add the todo.get_items service (#103285)

This commit is contained in:
Allen Porter 2023-11-15 02:41:29 -08:00 committed by GitHub
parent bba2734a5c
commit 5f13faac76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 4 deletions

View File

@ -11,7 +11,7 @@ from homeassistant.components import frontend, websocket_api
from homeassistant.components.websocket_api import ERR_NOT_FOUND, ERR_NOT_SUPPORTED
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
@ -58,7 +58,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
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}
{TodoItemStatus.NEEDS_ACTION, TodoItemStatus.COMPLETED},
),
}
),
@ -77,6 +77,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
_async_remove_todo_items,
required_features=[TodoListEntityFeature.DELETE_TODO_ITEM],
)
component.async_register_entity_service(
"get_items",
cv.make_entity_service_schema(
{
vol.Optional("status"): vol.All(
cv.ensure_list,
[vol.In({TodoItemStatus.NEEDS_ACTION, TodoItemStatus.COMPLETED})],
),
}
),
_async_get_todo_items,
supports_response=SupportsResponse.ONLY,
)
await component.async_setup(config)
return True
@ -258,3 +271,16 @@ async def _async_remove_todo_items(entity: TodoListEntity, call: ServiceCall) ->
raise ValueError(f"Unable to find To-do item '{item}")
uids.append(found.uid)
await entity.async_delete_todo_items(uids=uids)
async def _async_get_todo_items(
entity: TodoListEntity, call: ServiceCall
) -> dict[str, Any]:
"""Return items in the To-do list."""
return {
"items": [
dataclasses.asdict(item)
for item in entity.todo_items or ()
if not (statuses := call.data.get("status")) or item.status in statuses
]
}

View File

@ -1,3 +1,18 @@
get_items:
target:
entity:
domain: todo
fields:
status:
example: "needs_action"
default: needs_action
selector:
select:
translation_key: status
options:
- needs_action
- completed
multiple: true
add_item:
target:
entity:

View File

@ -6,6 +6,16 @@
}
},
"services": {
"get_items": {
"name": "Get to-do list items",
"description": "Get items on a to-do list.",
"fields": {
"status": {
"name": "Status",
"description": "Only return to-do items with the specified statuses. Returns not completed actions by default."
}
}
},
"add_item": {
"name": "Add to-do list item",
"description": "Add a new to-do list item.",

View File

@ -33,6 +33,16 @@ from tests.common import (
from tests.typing import WebSocketGenerator
TEST_DOMAIN = "test"
ITEM_1 = {
"uid": "1",
"summary": "Item #1",
"status": "needs_action",
}
ITEM_2 = {
"uid": "2",
"summary": "Item #2",
"status": "completed",
}
class MockFlow(ConfigFlow):
@ -182,12 +192,63 @@ async def test_list_todo_items(
assert resp.get("success")
assert resp.get("result") == {
"items": [
{"summary": "Item #1", "uid": "1", "status": "needs_action"},
{"summary": "Item #2", "uid": "2", "status": "completed"},
ITEM_1,
ITEM_2,
]
}
@pytest.mark.parametrize(
("service_data", "expected_items"),
[
({}, [ITEM_1, ITEM_2]),
(
[
{"status": [TodoItemStatus.COMPLETED, TodoItemStatus.NEEDS_ACTION]},
[ITEM_1, ITEM_2],
]
),
(
[
{"status": [TodoItemStatus.NEEDS_ACTION]},
[ITEM_1],
]
),
(
[
{"status": [TodoItemStatus.COMPLETED]},
[ITEM_2],
]
),
],
)
async def test_get_items_service(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
test_entity: TodoListEntity,
service_data: dict[str, Any],
expected_items: list[dict[str, Any]],
) -> None:
"""Test listing items in a To-do list from a service call."""
await create_mock_platform(hass, [test_entity])
state = hass.states.get("todo.entity1")
assert state
assert state.state == "1"
assert state.attributes == {"supported_features": 15}
result = await hass.services.async_call(
DOMAIN,
"get_items",
service_data,
target={"entity_id": "todo.entity1"},
blocking=True,
return_response=True,
)
assert result == {"todo.entity1": {"items": expected_items}}
async def test_unsupported_websocket(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,