Add service response data for listing calendar events (#94759)

* Add service response data for listing calendar events

Add the capability of response data for for the entity component.

* Rename input arguments and add service description

* Improve list events to be more user friendly

Allow the end date to be determined based on a relative time duration. Make the start time optional and set to "now". Add additional test coverage. Update demo calendar to actually perform date range checks.

* Wrap docstrings properly.

* Increase test coverage

* Update to use new API calls

* Readability improvements

* Wrap docstrings

* Require at least one of end or duration

* Check for multiple entity matches earlier in the request

* Update documentation strings
This commit is contained in:
Allen Porter 2023-06-23 20:34:34 -07:00 committed by GitHub
parent 65454c945d
commit b9b5fe6be8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 331 additions and 31 deletions

View file

@ -2,7 +2,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable, Iterable
from collections.abc import Awaitable, Callable, Coroutine, Iterable
import dataclasses
from enum import Enum
from functools import cache, partial, wraps
@ -26,7 +26,13 @@ from homeassistant.const import (
ENTITY_MATCH_ALL,
ENTITY_MATCH_NONE,
)
from homeassistant.core import Context, HomeAssistant, ServiceCall, callback
from homeassistant.core import (
Context,
HomeAssistant,
ServiceCall,
ServiceResponse,
callback,
)
from homeassistant.exceptions import (
HomeAssistantError,
TemplateError,
@ -672,10 +678,10 @@ def async_set_service_schema(
async def entity_service_call( # noqa: C901
hass: HomeAssistant,
platforms: Iterable[EntityPlatform],
func: str | Callable[..., Any],
func: str | Callable[..., Coroutine[Any, Any, ServiceResponse]],
call: ServiceCall,
required_features: Iterable[int] | None = None,
) -> None:
) -> ServiceResponse | None:
"""Handle an entity service call.
Calls all platforms simultaneously.
@ -791,7 +797,16 @@ async def entity_service_call( # noqa: C901
entities.append(entity)
if not entities:
return
if call.return_response:
raise HomeAssistantError(
"Service call requested response data but did not match any entities"
)
return None
if call.return_response and len(entities) != 1:
raise HomeAssistantError(
"Service call requested response data but matched more than one entity"
)
done, pending = await asyncio.wait(
[
@ -804,8 +819,10 @@ async def entity_service_call( # noqa: C901
]
)
assert not pending
for future in done:
future.result() # pop exception if have
response_data: ServiceResponse | None
for task in done:
response_data = task.result() # pop exception if have
tasks: list[asyncio.Task[None]] = []
@ -824,28 +841,32 @@ async def entity_service_call( # noqa: C901
for future in done:
future.result() # pop exception if have
return response_data if call.return_response else None
async def _handle_entity_call(
hass: HomeAssistant,
entity: Entity,
func: str | Callable[..., Any],
func: str | Callable[..., Coroutine[Any, Any, ServiceResponse]],
data: dict | ServiceCall,
context: Context,
) -> None:
) -> ServiceResponse:
"""Handle calling service method."""
entity.async_set_context(context)
task: asyncio.Future[ServiceResponse] | None
if isinstance(func, str):
result = hass.async_run_job(
task = hass.async_run_job(
partial(getattr(entity, func), **data) # type: ignore[arg-type]
)
else:
result = hass.async_run_job(func, entity, data)
task = hass.async_run_job(func, entity, data)
# Guard because callback functions do not return a task when passed to
# async_run_job.
if result is not None:
await result
result: ServiceResponse | None = None
if task is not None:
result = await task
if asyncio.iscoroutine(result):
_LOGGER.error(
@ -856,7 +877,9 @@ async def _handle_entity_call(
func,
entity.entity_id,
)
await result
result = await result
return result
@bind_hass