Refactor logbook data to use a dataclass (#89534)

This commit is contained in:
J. Nick Koston 2023-03-11 01:45:27 -10:00 committed by GitHub
parent fccdd7b102
commit 01e1221443
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 39 additions and 27 deletions

View file

@ -19,7 +19,7 @@ from homeassistant.const import (
ATTR_NAME, ATTR_NAME,
EVENT_LOGBOOK_ENTRY, EVENT_LOGBOOK_ENTRY,
) )
from homeassistant.core import Context, Event, HomeAssistant, ServiceCall, callback from homeassistant.core import Context, HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entityfilter import ( from homeassistant.helpers.entityfilter import (
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA,
@ -35,7 +35,6 @@ from . import rest_api, websocket_api
from .const import ( # noqa: F401 from .const import ( # noqa: F401
ATTR_MESSAGE, ATTR_MESSAGE,
DOMAIN, DOMAIN,
LOGBOOK_ENTITIES_FILTER,
LOGBOOK_ENTRY_CONTEXT_ID, LOGBOOK_ENTRY_CONTEXT_ID,
LOGBOOK_ENTRY_DOMAIN, LOGBOOK_ENTRY_DOMAIN,
LOGBOOK_ENTRY_ENTITY_ID, LOGBOOK_ENTRY_ENTITY_ID,
@ -43,9 +42,8 @@ from .const import ( # noqa: F401
LOGBOOK_ENTRY_MESSAGE, LOGBOOK_ENTRY_MESSAGE,
LOGBOOK_ENTRY_NAME, LOGBOOK_ENTRY_NAME,
LOGBOOK_ENTRY_SOURCE, LOGBOOK_ENTRY_SOURCE,
LOGBOOK_FILTERS,
) )
from .models import LazyEventPartialState # noqa: F401 from .models import LazyEventPartialState, LogbookConfig
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA {DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA
@ -97,7 +95,6 @@ def async_log_entry(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Logbook setup.""" """Logbook setup."""
hass.data[DOMAIN] = {}
@callback @callback
def log_message(service: ServiceCall) -> None: def log_message(service: ServiceCall) -> None:
@ -134,8 +131,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
else: else:
filters = None filters = None
entities_filter = None entities_filter = None
hass.data[LOGBOOK_FILTERS] = filters
hass.data[LOGBOOK_ENTITIES_FILTER] = entities_filter external_events: dict[
str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]]
] = {}
hass.data[DOMAIN] = LogbookConfig(external_events, filters, entities_filter)
websocket_api.async_setup(hass) websocket_api.async_setup(hass)
rest_api.async_setup(hass, config, filters, entities_filter) rest_api.async_setup(hass, config, filters, entities_filter)
hass.services.async_register(DOMAIN, "log", log_message, schema=LOG_MESSAGE_SCHEMA) hass.services.async_register(DOMAIN, "log", log_message, schema=LOG_MESSAGE_SCHEMA)
@ -149,14 +149,16 @@ async def _process_logbook_platform(
hass: HomeAssistant, domain: str, platform: Any hass: HomeAssistant, domain: str, platform: Any
) -> None: ) -> None:
"""Process a logbook platform.""" """Process a logbook platform."""
logbook_config: LogbookConfig = hass.data[DOMAIN]
external_events = logbook_config.external_events
@callback @callback
def _async_describe_event( def _async_describe_event(
domain: str, domain: str,
event_name: str, event_name: str,
describe_callback: Callable[[Event], dict[str, Any]], describe_callback: Callable[[LazyEventPartialState], dict[str, Any]],
) -> None: ) -> None:
"""Teach logbook how to describe a new event.""" """Teach logbook how to describe a new event."""
hass.data[DOMAIN][event_name] = (domain, describe_callback) external_events[event_name] = (domain, describe_callback)
platform.async_describe_events(hass, _async_describe_event) platform.async_describe_events(hass, _async_describe_event)

View file

@ -44,6 +44,3 @@ AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED}
# Events that are built-in to the logbook or core # Events that are built-in to the logbook or core
BUILT_IN_EVENTS = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE} BUILT_IN_EVENTS = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE}
LOGBOOK_FILTERS = "logbook_filters"
LOGBOOK_ENTITIES_FILTER = "entities_filter"

View file

@ -27,7 +27,7 @@ from homeassistant.helpers.entityfilter import EntityFilter
from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.event import async_track_state_change_event
from .const import ALWAYS_CONTINUOUS_DOMAINS, AUTOMATION_EVENTS, BUILT_IN_EVENTS, DOMAIN from .const import ALWAYS_CONTINUOUS_DOMAINS, AUTOMATION_EVENTS, BUILT_IN_EVENTS, DOMAIN
from .models import LazyEventPartialState from .models import LogbookConfig
def async_filter_entities(hass: HomeAssistant, entity_ids: list[str]) -> list[str]: def async_filter_entities(hass: HomeAssistant, entity_ids: list[str]) -> list[str]:
@ -63,9 +63,8 @@ def async_determine_event_types(
hass: HomeAssistant, entity_ids: list[str] | None, device_ids: list[str] | None hass: HomeAssistant, entity_ids: list[str] | None, device_ids: list[str] | None
) -> tuple[str, ...]: ) -> tuple[str, ...]:
"""Reduce the event types based on the entity ids and device ids.""" """Reduce the event types based on the entity ids and device ids."""
external_events: dict[ logbook_config: LogbookConfig = hass.data[DOMAIN]
str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] external_events = logbook_config.external_events
] = hass.data.get(DOMAIN, {})
if not entity_ids and not device_ids: if not entity_ids and not device_ids:
return (*BUILT_IN_EVENTS, *external_events) return (*BUILT_IN_EVENTS, *external_events)

View file

@ -1,11 +1,13 @@
"""Event parser and human readable log generator.""" """Event parser and human readable log generator."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, cast from typing import Any, cast
from sqlalchemy.engine.row import Row from sqlalchemy.engine.row import Row
from homeassistant.components.recorder.filters import Filters
from homeassistant.components.recorder.models import ( from homeassistant.components.recorder.models import (
bytes_to_ulid_or_none, bytes_to_ulid_or_none,
bytes_to_uuid_hex_or_none, bytes_to_uuid_hex_or_none,
@ -14,11 +16,23 @@ from homeassistant.components.recorder.models import (
) )
from homeassistant.const import ATTR_ICON, EVENT_STATE_CHANGED from homeassistant.const import ATTR_ICON, EVENT_STATE_CHANGED
from homeassistant.core import Context, Event, State, callback from homeassistant.core import Context, Event, State, callback
from homeassistant.helpers.entityfilter import EntityFilter
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.json import json_loads from homeassistant.util.json import json_loads
from homeassistant.util.ulid import ulid_to_bytes from homeassistant.util.ulid import ulid_to_bytes
@dataclass
class LogbookConfig:
"""Configuration for the logbook integration."""
external_events: dict[
str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]]
]
sqlalchemy_filter: Filters | None = None
entity_filter: EntityFilter | None = None
class LazyEventPartialState: class LazyEventPartialState:
"""A lazy version of core Event with limited State joined in.""" """A lazy version of core Event with limited State joined in."""

View file

@ -52,10 +52,9 @@ from .const import (
LOGBOOK_ENTRY_SOURCE, LOGBOOK_ENTRY_SOURCE,
LOGBOOK_ENTRY_STATE, LOGBOOK_ENTRY_STATE,
LOGBOOK_ENTRY_WHEN, LOGBOOK_ENTRY_WHEN,
LOGBOOK_FILTERS,
) )
from .helpers import is_sensor_continuous from .helpers import is_sensor_continuous
from .models import EventAsRow, LazyEventPartialState, async_event_to_row from .models import EventAsRow, LazyEventPartialState, LogbookConfig, async_event_to_row
from .queries import statement_for_request from .queries import statement_for_request
from .queries.common import PSEUDO_EVENT_STATE_CHANGED from .queries.common import PSEUDO_EVENT_STATE_CHANGED
@ -97,16 +96,14 @@ class EventProcessor:
self.entity_ids = entity_ids self.entity_ids = entity_ids
self.device_ids = device_ids self.device_ids = device_ids
self.context_id = context_id self.context_id = context_id
self.filters: Filters | None = hass.data[LOGBOOK_FILTERS] logbook_config: LogbookConfig = hass.data[DOMAIN]
self.filters: Filters | None = logbook_config.sqlalchemy_filter
format_time = ( format_time = (
_row_time_fired_timestamp if timestamp else _row_time_fired_isoformat _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat
) )
external_events: dict[
str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]]
] = hass.data.get(DOMAIN, {})
self.logbook_run = LogbookRun( self.logbook_run = LogbookRun(
context_lookup=ContextLookup(hass), context_lookup=ContextLookup(hass),
external_events=external_events, external_events=logbook_config.external_events,
event_cache=EventCache({}), event_cache=EventCache({}),
entity_name_cache=EntityNameCache(self.hass), entity_name_cache=EntityNameCache(self.hass),
include_entity_name=include_entity_name, include_entity_name=include_entity_name,

View file

@ -20,13 +20,13 @@ from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.json import JSON_DUMP from homeassistant.helpers.json import JSON_DUMP
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .const import LOGBOOK_ENTITIES_FILTER from .const import DOMAIN
from .helpers import ( from .helpers import (
async_determine_event_types, async_determine_event_types,
async_filter_entities, async_filter_entities,
async_subscribe_events, async_subscribe_events,
) )
from .models import async_event_to_row from .models import LogbookConfig, async_event_to_row
from .processor import EventProcessor from .processor import EventProcessor
MAX_PENDING_LOGBOOK_EVENTS = 2048 MAX_PENDING_LOGBOOK_EVENTS = 2048
@ -361,7 +361,8 @@ async def ws_event_stream(
entities_filter: EntityFilter | None = None entities_filter: EntityFilter | None = None
if not event_processor.limited_select: if not event_processor.limited_select:
entities_filter = hass.data[LOGBOOK_ENTITIES_FILTER] logbook_config: LogbookConfig = hass.data[DOMAIN]
entities_filter = logbook_config.entity_filter
async_subscribe_events( async_subscribe_events(
hass, hass,

View file

@ -6,6 +6,7 @@ from typing import Any
from homeassistant.components import logbook from homeassistant.components import logbook
from homeassistant.components.logbook import processor from homeassistant.components.logbook import processor
from homeassistant.components.logbook.models import LogbookConfig
from homeassistant.components.recorder.models import ( from homeassistant.components.recorder.models import (
process_timestamp_to_utc_isoformat, process_timestamp_to_utc_isoformat,
ulid_to_bytes_or_none, ulid_to_bytes_or_none,
@ -64,7 +65,8 @@ def mock_humanify(hass_, rows):
ent_reg = er.async_get(hass_) ent_reg = er.async_get(hass_)
event_cache = processor.EventCache({}) event_cache = processor.EventCache({})
context_lookup = processor.ContextLookup(hass_) context_lookup = processor.ContextLookup(hass_)
external_events = hass_.data.get(logbook.DOMAIN, {}) logbook_config = hass_.data.get(logbook.DOMAIN, LogbookConfig({}, None, None))
external_events = logbook_config.external_events
logbook_run = processor.LogbookRun( logbook_run = processor.LogbookRun(
context_lookup, context_lookup,
external_events, external_events,