mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 13:47:35 +00:00
Complete helpers.service type hints (#40193)
* Complete helpers.service type hints * Update homeassistant/helpers/service.py Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> * Handle None entity.supported_features
This commit is contained in:
parent
5dcaeebdac
commit
167490b71c
@ -546,7 +546,7 @@ class EntityPlatform:
|
|||||||
|
|
||||||
async def handle_service(call: ServiceCall) -> None:
|
async def handle_service(call: ServiceCall) -> None:
|
||||||
"""Handle the service."""
|
"""Handle the service."""
|
||||||
await service.entity_service_call( # type: ignore
|
await service.entity_service_call(
|
||||||
self.hass,
|
self.hass,
|
||||||
[
|
[
|
||||||
plf
|
plf
|
||||||
|
@ -13,6 +13,7 @@ from typing import (
|
|||||||
Optional,
|
Optional,
|
||||||
Set,
|
Set,
|
||||||
Tuple,
|
Tuple,
|
||||||
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -43,10 +44,9 @@ from homeassistant.util.yaml.loader import JSON_TYPE
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from homeassistant.helpers.entity import Entity # noqa
|
from homeassistant.helpers.entity import Entity # noqa
|
||||||
|
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||||
|
|
||||||
|
|
||||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
|
||||||
|
|
||||||
CONF_SERVICE_ENTITY_ID = "entity_id"
|
CONF_SERVICE_ENTITY_ID = "entity_id"
|
||||||
CONF_SERVICE_DATA = "data"
|
CONF_SERVICE_DATA = "data"
|
||||||
CONF_SERVICE_DATA_TEMPLATE = "data_template"
|
CONF_SERVICE_DATA_TEMPLATE = "data_template"
|
||||||
@ -340,7 +340,13 @@ def async_set_service_schema(
|
|||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def entity_service_call(hass, platforms, func, call, required_features=None):
|
async def entity_service_call(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
platforms: Iterable["EntityPlatform"],
|
||||||
|
func: Union[str, Callable[..., Any]],
|
||||||
|
call: ha.ServiceCall,
|
||||||
|
required_features: Optional[Iterable[int]] = None,
|
||||||
|
) -> None:
|
||||||
"""Handle an entity service call.
|
"""Handle an entity service call.
|
||||||
|
|
||||||
Calls all platforms simultaneously.
|
Calls all platforms simultaneously.
|
||||||
@ -349,7 +355,9 @@ async def entity_service_call(hass, platforms, func, call, required_features=Non
|
|||||||
user = await hass.auth.async_get_user(call.context.user_id)
|
user = await hass.auth.async_get_user(call.context.user_id)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise UnknownUser(context=call.context)
|
raise UnknownUser(context=call.context)
|
||||||
entity_perms = user.permissions.check_entity
|
entity_perms: Optional[
|
||||||
|
Callable[[str, str], bool]
|
||||||
|
] = user.permissions.check_entity
|
||||||
else:
|
else:
|
||||||
entity_perms = None
|
entity_perms = None
|
||||||
|
|
||||||
@ -361,7 +369,7 @@ async def entity_service_call(hass, platforms, func, call, required_features=Non
|
|||||||
|
|
||||||
# If the service function is a string, we'll pass it the service call data
|
# If the service function is a string, we'll pass it the service call data
|
||||||
if isinstance(func, str):
|
if isinstance(func, str):
|
||||||
data = {
|
data: Union[Dict, ha.ServiceCall] = {
|
||||||
key: val
|
key: val
|
||||||
for key, val in call.data.items()
|
for key, val in call.data.items()
|
||||||
if key not in cv.ENTITY_SERVICE_FIELDS
|
if key not in cv.ENTITY_SERVICE_FIELDS
|
||||||
@ -373,7 +381,7 @@ async def entity_service_call(hass, platforms, func, call, required_features=Non
|
|||||||
# Check the permissions
|
# Check the permissions
|
||||||
|
|
||||||
# A list with entities to call the service on.
|
# A list with entities to call the service on.
|
||||||
entity_candidates = []
|
entity_candidates: List["Entity"] = []
|
||||||
|
|
||||||
if entity_perms is None:
|
if entity_perms is None:
|
||||||
for platform in platforms:
|
for platform in platforms:
|
||||||
@ -435,9 +443,12 @@ async def entity_service_call(hass, platforms, func, call, required_features=Non
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip entities that don't have the required feature.
|
# Skip entities that don't have the required feature.
|
||||||
if required_features is not None and not any(
|
if required_features is not None and (
|
||||||
entity.supported_features & feature_set == feature_set
|
entity.supported_features is None
|
||||||
for feature_set in required_features
|
or not any(
|
||||||
|
entity.supported_features & feature_set == feature_set
|
||||||
|
for feature_set in required_features
|
||||||
|
)
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -476,12 +487,18 @@ async def entity_service_call(hass, platforms, func, call, required_features=Non
|
|||||||
future.result() # pop exception if have
|
future.result() # pop exception if have
|
||||||
|
|
||||||
|
|
||||||
async def _handle_entity_call(hass, entity, func, data, context):
|
async def _handle_entity_call(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
entity: "Entity",
|
||||||
|
func: Union[str, Callable[..., Any]],
|
||||||
|
data: Union[Dict, ha.ServiceCall],
|
||||||
|
context: ha.Context,
|
||||||
|
) -> None:
|
||||||
"""Handle calling service method."""
|
"""Handle calling service method."""
|
||||||
entity.async_set_context(context)
|
entity.async_set_context(context)
|
||||||
|
|
||||||
if isinstance(func, str):
|
if isinstance(func, str):
|
||||||
result = hass.async_add_job(partial(getattr(entity, func), **data))
|
result = hass.async_add_job(partial(getattr(entity, func), **data)) # type: ignore
|
||||||
else:
|
else:
|
||||||
result = hass.async_add_job(func, entity, data)
|
result = hass.async_add_job(func, entity, data)
|
||||||
|
|
||||||
@ -495,7 +512,7 @@ async def _handle_entity_call(hass, entity, func, data, context):
|
|||||||
func,
|
func,
|
||||||
entity.entity_id,
|
entity.entity_id,
|
||||||
)
|
)
|
||||||
await result
|
await result # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
@ -530,12 +547,12 @@ def async_register_admin_service(
|
|||||||
def verify_domain_control(hass: HomeAssistantType, domain: str) -> Callable:
|
def verify_domain_control(hass: HomeAssistantType, domain: str) -> Callable:
|
||||||
"""Ensure permission to access any entity under domain in service call."""
|
"""Ensure permission to access any entity under domain in service call."""
|
||||||
|
|
||||||
def decorator(service_handler: Callable) -> Callable:
|
def decorator(service_handler: Callable[[ha.ServiceCall], Any]) -> Callable:
|
||||||
"""Decorate."""
|
"""Decorate."""
|
||||||
if not asyncio.iscoroutinefunction(service_handler):
|
if not asyncio.iscoroutinefunction(service_handler):
|
||||||
raise HomeAssistantError("Can only decorate async functions.")
|
raise HomeAssistantError("Can only decorate async functions.")
|
||||||
|
|
||||||
async def check_permissions(call):
|
async def check_permissions(call: ha.ServiceCall) -> Any:
|
||||||
"""Check user permission and raise before call if unauthorized."""
|
"""Check user permission and raise before call if unauthorized."""
|
||||||
if not call.context.user_id:
|
if not call.context.user_id:
|
||||||
return await service_handler(call)
|
return await service_handler(call)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user