"""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 ." ) 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) )