mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Improve api typing (#108307)
This commit is contained in:
parent
a670ac25fd
commit
7c6fe31505
@ -4,6 +4,7 @@ from asyncio import shield, timeout
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from aiohttp.web_exceptions import HTTPBadRequest
|
from aiohttp.web_exceptions import HTTPBadRequest
|
||||||
@ -30,7 +31,7 @@ from homeassistant.const import (
|
|||||||
URL_API_TEMPLATE,
|
URL_API_TEMPLATE,
|
||||||
)
|
)
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import Event, HomeAssistant
|
||||||
from homeassistant.exceptions import (
|
from homeassistant.exceptions import (
|
||||||
InvalidEntityFormatError,
|
InvalidEntityFormatError,
|
||||||
InvalidStateError,
|
InvalidStateError,
|
||||||
@ -92,7 +93,7 @@ class APIStatusView(HomeAssistantView):
|
|||||||
name = "api:status"
|
name = "api:status"
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def get(self, request):
|
def get(self, request: web.Request) -> web.Response:
|
||||||
"""Retrieve if API is running."""
|
"""Retrieve if API is running."""
|
||||||
return self.json_message("API running.")
|
return self.json_message("API running.")
|
||||||
|
|
||||||
@ -124,14 +125,15 @@ class APIEventStream(HomeAssistantView):
|
|||||||
@require_admin
|
@require_admin
|
||||||
async def get(self, request):
|
async def get(self, request):
|
||||||
"""Provide a streaming interface for the event bus."""
|
"""Provide a streaming interface for the event bus."""
|
||||||
hass = request.app["hass"]
|
hass: HomeAssistant = request.app["hass"]
|
||||||
stop_obj = object()
|
stop_obj = object()
|
||||||
to_write = asyncio.Queue()
|
to_write: asyncio.Queue[object | str] = asyncio.Queue()
|
||||||
|
|
||||||
if restrict := request.query.get("restrict"):
|
restrict: list[str] | None = None
|
||||||
restrict = restrict.split(",") + [EVENT_HOMEASSISTANT_STOP]
|
if restrict_str := request.query.get("restrict"):
|
||||||
|
restrict = restrict_str.split(",") + [EVENT_HOMEASSISTANT_STOP]
|
||||||
|
|
||||||
async def forward_events(event):
|
async def forward_events(event: Event) -> None:
|
||||||
"""Forward events to the open request."""
|
"""Forward events to the open request."""
|
||||||
if restrict and event.event_type not in restrict:
|
if restrict and event.event_type not in restrict:
|
||||||
return
|
return
|
||||||
@ -188,9 +190,10 @@ class APIConfigView(HomeAssistantView):
|
|||||||
name = "api:config"
|
name = "api:config"
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def get(self, request):
|
def get(self, request: web.Request) -> web.Response:
|
||||||
"""Get current configuration."""
|
"""Get current configuration."""
|
||||||
return self.json(request.app["hass"].config.as_dict())
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
return self.json(hass.config.as_dict())
|
||||||
|
|
||||||
|
|
||||||
class APIStatesView(HomeAssistantView):
|
class APIStatesView(HomeAssistantView):
|
||||||
@ -243,9 +246,10 @@ class APIEntityStateView(HomeAssistantView):
|
|||||||
)
|
)
|
||||||
return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND)
|
return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
async def post(self, request, entity_id):
|
async def post(self, request: web.Request, entity_id: str) -> web.Response:
|
||||||
"""Update state of entity."""
|
"""Update state of entity."""
|
||||||
if not request["hass_user"].is_admin:
|
user: User = request["hass_user"]
|
||||||
|
if not user.is_admin:
|
||||||
raise Unauthorized(entity_id=entity_id)
|
raise Unauthorized(entity_id=entity_id)
|
||||||
hass: HomeAssistant = request.app["hass"]
|
hass: HomeAssistant = request.app["hass"]
|
||||||
try:
|
try:
|
||||||
@ -275,18 +279,20 @@ class APIEntityStateView(HomeAssistantView):
|
|||||||
|
|
||||||
# Read the state back for our response
|
# Read the state back for our response
|
||||||
status_code = HTTPStatus.CREATED if is_new_state else HTTPStatus.OK
|
status_code = HTTPStatus.CREATED if is_new_state else HTTPStatus.OK
|
||||||
resp = self.json(hass.states.get(entity_id).as_dict(), status_code)
|
assert (state := hass.states.get(entity_id))
|
||||||
|
resp = self.json(state.as_dict(), status_code)
|
||||||
|
|
||||||
resp.headers.add("Location", f"/api/states/{entity_id}")
|
resp.headers.add("Location", f"/api/states/{entity_id}")
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def delete(self, request, entity_id):
|
def delete(self, request: web.Request, entity_id: str) -> web.Response:
|
||||||
"""Remove entity."""
|
"""Remove entity."""
|
||||||
if not request["hass_user"].is_admin:
|
if not request["hass_user"].is_admin:
|
||||||
raise Unauthorized(entity_id=entity_id)
|
raise Unauthorized(entity_id=entity_id)
|
||||||
if request.app["hass"].states.async_remove(entity_id):
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
if hass.states.async_remove(entity_id):
|
||||||
return self.json_message("Entity removed.")
|
return self.json_message("Entity removed.")
|
||||||
return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND)
|
return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
@ -298,9 +304,10 @@ class APIEventListenersView(HomeAssistantView):
|
|||||||
name = "api:event-listeners"
|
name = "api:event-listeners"
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def get(self, request):
|
def get(self, request: web.Request) -> web.Response:
|
||||||
"""Get event listeners."""
|
"""Get event listeners."""
|
||||||
return self.json(async_events_json(request.app["hass"]))
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
return self.json(async_events_json(hass))
|
||||||
|
|
||||||
|
|
||||||
class APIEventView(HomeAssistantView):
|
class APIEventView(HomeAssistantView):
|
||||||
@ -310,11 +317,11 @@ class APIEventView(HomeAssistantView):
|
|||||||
name = "api:event"
|
name = "api:event"
|
||||||
|
|
||||||
@require_admin
|
@require_admin
|
||||||
async def post(self, request, event_type):
|
async def post(self, request: web.Request, event_type: str) -> web.Response:
|
||||||
"""Fire events."""
|
"""Fire events."""
|
||||||
body = await request.text()
|
body = await request.text()
|
||||||
try:
|
try:
|
||||||
event_data = json_loads(body) if body else None
|
event_data: Any = json_loads(body) if body else None
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return self.json_message(
|
return self.json_message(
|
||||||
"Event data should be valid JSON.", HTTPStatus.BAD_REQUEST
|
"Event data should be valid JSON.", HTTPStatus.BAD_REQUEST
|
||||||
@ -327,14 +334,15 @@ class APIEventView(HomeAssistantView):
|
|||||||
|
|
||||||
# Special case handling for event STATE_CHANGED
|
# Special case handling for event STATE_CHANGED
|
||||||
# We will try to convert state dicts back to State objects
|
# We will try to convert state dicts back to State objects
|
||||||
if event_type == ha.EVENT_STATE_CHANGED and event_data:
|
if event_type == EVENT_STATE_CHANGED and event_data:
|
||||||
for key in ("old_state", "new_state"):
|
for key in ("old_state", "new_state"):
|
||||||
state = ha.State.from_dict(event_data.get(key))
|
state = ha.State.from_dict(event_data[key])
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
event_data[key] = state
|
event_data[key] = state
|
||||||
|
|
||||||
request.app["hass"].bus.async_fire(
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
hass.bus.async_fire(
|
||||||
event_type, event_data, ha.EventOrigin.remote, self.context(request)
|
event_type, event_data, ha.EventOrigin.remote, self.context(request)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -347,9 +355,10 @@ class APIServicesView(HomeAssistantView):
|
|||||||
url = URL_API_SERVICES
|
url = URL_API_SERVICES
|
||||||
name = "api:services"
|
name = "api:services"
|
||||||
|
|
||||||
async def get(self, request):
|
async def get(self, request: web.Request) -> web.Response:
|
||||||
"""Get registered services."""
|
"""Get registered services."""
|
||||||
services = await async_services_json(request.app["hass"])
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
services = await async_services_json(hass)
|
||||||
return self.json(services)
|
return self.json(services)
|
||||||
|
|
||||||
|
|
||||||
@ -359,12 +368,14 @@ class APIDomainServicesView(HomeAssistantView):
|
|||||||
url = "/api/services/{domain}/{service}"
|
url = "/api/services/{domain}/{service}"
|
||||||
name = "api:domain-services"
|
name = "api:domain-services"
|
||||||
|
|
||||||
async def post(self, request, domain, service):
|
async def post(
|
||||||
|
self, request: web.Request, domain: str, service: str
|
||||||
|
) -> web.Response:
|
||||||
"""Call a service.
|
"""Call a service.
|
||||||
|
|
||||||
Returns a list of changed states.
|
Returns a list of changed states.
|
||||||
"""
|
"""
|
||||||
hass: ha.HomeAssistant = request.app["hass"]
|
hass: HomeAssistant = request.app["hass"]
|
||||||
body = await request.text()
|
body = await request.text()
|
||||||
try:
|
try:
|
||||||
data = json_loads(body) if body else None
|
data = json_loads(body) if body else None
|
||||||
@ -384,14 +395,20 @@ class APIDomainServicesView(HomeAssistantView):
|
|||||||
changed_states.append(state.json_fragment)
|
changed_states.append(state.json_fragment)
|
||||||
|
|
||||||
cancel_listen = hass.bus.async_listen(
|
cancel_listen = hass.bus.async_listen(
|
||||||
EVENT_STATE_CHANGED, _async_save_changed_entities, run_immediately=True
|
EVENT_STATE_CHANGED,
|
||||||
|
_async_save_changed_entities, # type: ignore[arg-type]
|
||||||
|
run_immediately=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# shield the service call from cancellation on connection drop
|
# shield the service call from cancellation on connection drop
|
||||||
await shield(
|
await shield(
|
||||||
hass.services.async_call(
|
hass.services.async_call(
|
||||||
domain, service, data, blocking=True, context=context
|
domain,
|
||||||
|
service,
|
||||||
|
data, # type: ignore[arg-type]
|
||||||
|
blocking=True,
|
||||||
|
context=context,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except (vol.Invalid, ServiceNotFound) as ex:
|
except (vol.Invalid, ServiceNotFound) as ex:
|
||||||
@ -409,9 +426,10 @@ class APIComponentsView(HomeAssistantView):
|
|||||||
name = "api:components"
|
name = "api:components"
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def get(self, request):
|
def get(self, request: web.Request) -> web.Response:
|
||||||
"""Get current loaded components."""
|
"""Get current loaded components."""
|
||||||
return self.json(request.app["hass"].config.components)
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
return self.json(hass.config.components)
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
@ -427,7 +445,7 @@ class APITemplateView(HomeAssistantView):
|
|||||||
name = "api:template"
|
name = "api:template"
|
||||||
|
|
||||||
@require_admin
|
@require_admin
|
||||||
async def post(self, request):
|
async def post(self, request: web.Request) -> web.Response:
|
||||||
"""Render a template."""
|
"""Render a template."""
|
||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
@ -448,17 +466,18 @@ class APIErrorLog(HomeAssistantView):
|
|||||||
@require_admin
|
@require_admin
|
||||||
async def get(self, request):
|
async def get(self, request):
|
||||||
"""Retrieve API error log."""
|
"""Retrieve API error log."""
|
||||||
return web.FileResponse(request.app["hass"].data[DATA_LOGGING])
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
return web.FileResponse(hass.data[DATA_LOGGING])
|
||||||
|
|
||||||
|
|
||||||
async def async_services_json(hass):
|
async def async_services_json(hass: HomeAssistant) -> list[dict[str, Any]]:
|
||||||
"""Generate services data to JSONify."""
|
"""Generate services data to JSONify."""
|
||||||
descriptions = await async_get_all_descriptions(hass)
|
descriptions = await async_get_all_descriptions(hass)
|
||||||
return [{"domain": key, "services": value} for key, value in descriptions.items()]
|
return [{"domain": key, "services": value} for key, value in descriptions.items()]
|
||||||
|
|
||||||
|
|
||||||
@ha.callback
|
@ha.callback
|
||||||
def async_events_json(hass):
|
def async_events_json(hass: HomeAssistant) -> list[dict[str, Any]]:
|
||||||
"""Generate event data to JSONify."""
|
"""Generate event data to JSONify."""
|
||||||
return [
|
return [
|
||||||
{"event": key, "listener_count": value}
|
{"event": key, "listener_count": value}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user