core/homeassistant/components/logbook/rest_api.py

121 lines
3.9 KiB
Python

"""Event parser and human readable log generator."""
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
from http import HTTPStatus
from typing import Any, cast
from aiohttp import web
import voluptuous as vol
from homeassistant.components.http import KEY_HASS, HomeAssistantView
from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.filters import Filters
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import InvalidEntityFormatError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util
from .helpers import async_determine_event_types
from .processor import EventProcessor
@callback
def async_setup(
hass: HomeAssistant,
conf: ConfigType,
filters: Filters | None,
entities_filter: Callable[[str], bool] | None,
) -> None:
"""Set up the logbook rest API."""
hass.http.register_view(LogbookView(conf, filters, entities_filter))
class LogbookView(HomeAssistantView):
"""Handle logbook view requests."""
url = "/api/logbook"
name = "api:logbook"
extra_urls = ["/api/logbook/{datetime}"]
def __init__(
self,
config: dict[str, Any],
filters: Filters | None,
entities_filter: Callable[[str], bool] | None,
) -> None:
"""Initialize the logbook view."""
self.config = config
self.filters = filters
self.entities_filter = entities_filter
async def get(
self, request: web.Request, datetime: str | None = None
) -> web.Response:
"""Retrieve logbook entries."""
if datetime:
if (datetime_dt := dt_util.parse_datetime(datetime)) is None:
return self.json_message("Invalid datetime", HTTPStatus.BAD_REQUEST)
else:
datetime_dt = dt_util.start_of_local_day()
if (period_str := request.query.get("period")) is None:
period: int = 1
else:
period = int(period_str)
if entity_ids_str := request.query.get("entity"):
try:
entity_ids = cv.entity_ids(entity_ids_str)
except vol.Invalid:
raise InvalidEntityFormatError(
f"Invalid entity id(s) encountered: {entity_ids_str}. "
"Format should be <domain>.<object_id>"
) from vol.Invalid
else:
entity_ids = None
if (end_time_str := request.query.get("end_time")) is None:
start_day = dt_util.as_utc(datetime_dt) - timedelta(days=period - 1)
end_day = start_day + timedelta(days=period)
else:
start_day = datetime_dt
if (end_day_dt := dt_util.parse_datetime(end_time_str)) is None:
return self.json_message("Invalid end_time", HTTPStatus.BAD_REQUEST)
end_day = end_day_dt
hass = request.app[KEY_HASS]
context_id = request.query.get("context_id")
if entity_ids and context_id:
return self.json_message(
"Can't combine entity with context_id", HTTPStatus.BAD_REQUEST
)
event_types = async_determine_event_types(hass, entity_ids, None)
event_processor = EventProcessor(
hass,
event_types,
entity_ids,
None,
context_id,
timestamp=False,
include_entity_name=True,
)
def json_events() -> web.Response:
"""Fetch events and generate JSON."""
return self.json(
event_processor.get_events(
start_day,
end_day,
)
)
return cast(
web.Response, await get_instance(hass).async_add_executor_job(json_events)
)