From 0dc21504f5ba441bbe32c2e8b6c2868dbf09dc6a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 24 Sep 2023 14:45:06 +0200 Subject: [PATCH] Remove support for excluding attributes in recorder platforms (#100679) --- homeassistant/components/recorder/__init__.py | 22 ++----------------- homeassistant/components/recorder/const.py | 7 ------ homeassistant/components/recorder/core.py | 5 +---- .../components/recorder/db_schema.py | 10 +-------- .../table_managers/state_attributes.py | 6 +---- tests/components/recorder/test_init.py | 21 ++++++++++-------- tests/components/recorder/test_models.py | 2 +- 7 files changed, 18 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 72d825d9e78..1c00149192f 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -27,9 +27,7 @@ from .const import ( # noqa: F401 DOMAIN, EVENT_RECORDER_5MIN_STATISTICS_GENERATED, EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, - EXCLUDE_ATTRIBUTES, INTEGRATION_PLATFORM_COMPILE_STATISTICS, - INTEGRATION_PLATFORM_EXCLUDE_ATTRIBUTES, INTEGRATION_PLATFORMS_LOAD_IN_RECORDER_THREAD, SQLITE_URL_PREFIX, SupportedDialect, @@ -132,8 +130,6 @@ def is_entity_recorded(hass: HomeAssistant, entity_id: str) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the recorder.""" - exclude_attributes_by_domain: dict[str, set[str]] = {} - hass.data[EXCLUDE_ATTRIBUTES] = exclude_attributes_by_domain conf = config[DOMAIN] entity_filter = convert_include_exclude_filter(conf).get_filter() auto_purge = conf[CONF_AUTO_PURGE] @@ -161,7 +157,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: db_retry_wait=db_retry_wait, entity_filter=entity_filter, exclude_event_types=exclude_event_types, - exclude_attributes_by_domain=exclude_attributes_by_domain, ) instance.async_initialize() instance.async_register() @@ -170,17 +165,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: websocket_api.async_setup(hass) entity_registry.async_setup(hass) - await _async_setup_integration_platform( - hass, instance, exclude_attributes_by_domain - ) + await _async_setup_integration_platform(hass, instance) return await instance.async_db_ready async def _async_setup_integration_platform( - hass: HomeAssistant, - instance: Recorder, - exclude_attributes_by_domain: dict[str, set[str]], + hass: HomeAssistant, instance: Recorder ) -> None: """Set up a recorder integration platform.""" @@ -188,15 +179,6 @@ async def _async_setup_integration_platform( hass: HomeAssistant, domain: str, platform: Any ) -> None: """Process a recorder platform.""" - # We need to add this before as soon as the component is loaded - # to ensure by the time the state is recorded that the excluded - # attributes are known. This is safe to modify in the event loop - # since exclude_attributes_by_domain is never iterated over. - if exclude_attributes := getattr( - platform, INTEGRATION_PLATFORM_EXCLUDE_ATTRIBUTES, None - ): - exclude_attributes_by_domain[domain] = exclude_attributes(hass) - # If the platform has a compile_statistics method, we need to # add it to the recorder queue to be processed. if any( diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 724a9589680..7389cbf8ddf 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -40,10 +40,6 @@ ATTR_APPLY_FILTER = "apply_filter" KEEPALIVE_TIME = 30 - -EXCLUDE_ATTRIBUTES = f"{DOMAIN}_exclude_attributes_by_domain" - - STATISTICS_ROWS_SCHEMA_VERSION = 23 CONTEXT_ID_AS_BINARY_SCHEMA_VERSION = 36 EVENT_TYPE_IDS_SCHEMA_VERSION = 37 @@ -51,9 +47,6 @@ STATES_META_SCHEMA_VERSION = 38 LEGACY_STATES_EVENT_ID_INDEX_SCHEMA_VERSION = 28 - -INTEGRATION_PLATFORM_EXCLUDE_ATTRIBUTES = "exclude_attributes" - INTEGRATION_PLATFORM_COMPILE_STATISTICS = "compile_statistics" INTEGRATION_PLATFORM_VALIDATE_STATISTICS = "validate_statistics" INTEGRATION_PLATFORM_LIST_STATISTIC_IDS = "list_statistic_ids" diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 8aa2bce96b1..0e926ad2a22 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -177,7 +177,6 @@ class Recorder(threading.Thread): db_retry_wait: int, entity_filter: Callable[[str], bool], exclude_event_types: set[str], - exclude_attributes_by_domain: dict[str, set[str]], ) -> None: """Initialize the recorder.""" threading.Thread.__init__(self, name="Recorder") @@ -221,9 +220,7 @@ class Recorder(threading.Thread): self.event_data_manager = EventDataManager(self) self.event_type_manager = EventTypeManager(self) self.states_meta_manager = StatesMetaManager(self) - self.state_attributes_manager = StateAttributesManager( - self, exclude_attributes_by_domain - ) + self.state_attributes_manager = StateAttributesManager(self) self.statistics_meta_manager = StatisticsMetaManager(self) self.event_session: Session | None = None diff --git a/homeassistant/components/recorder/db_schema.py b/homeassistant/components/recorder/db_schema.py index e992a683cb1..17e34af1e11 100644 --- a/homeassistant/components/recorder/db_schema.py +++ b/homeassistant/components/recorder/db_schema.py @@ -39,7 +39,7 @@ from homeassistant.const import ( MAX_LENGTH_STATE_ENTITY_ID, MAX_LENGTH_STATE_STATE, ) -from homeassistant.core import Context, Event, EventOrigin, State, split_entity_id +from homeassistant.core import Context, Event, EventOrigin, State from homeassistant.helpers.entity import EntityInfo from homeassistant.helpers.json import JSON_DUMP, json_bytes, json_bytes_strip_null import homeassistant.util.dt as dt_util @@ -560,7 +560,6 @@ class StateAttributes(Base): def shared_attrs_bytes_from_event( event: Event, entity_sources: dict[str, EntityInfo], - exclude_attrs_by_domain: dict[str, set[str]], dialect: SupportedDialect | None, ) -> bytes: """Create shared_attrs from a state_changed event.""" @@ -568,14 +567,7 @@ class StateAttributes(Base): # None state means the state was removed from the state machine if state is None: return b"{}" - domain = split_entity_id(state.entity_id)[0] exclude_attrs = set(ALL_DOMAIN_EXCLUDE_ATTRS) - if base_platform_attrs := exclude_attrs_by_domain.get(domain): - exclude_attrs |= base_platform_attrs - if (entity_info := entity_sources.get(state.entity_id)) and ( - integration_attrs := exclude_attrs_by_domain.get(entity_info["domain"]) - ): - exclude_attrs |= integration_attrs if state_info := state.state_info: exclude_attrs |= state_info["unrecorded_attributes"] encoder = json_bytes_strip_null if dialect == PSQL_DIALECT else json_bytes diff --git a/homeassistant/components/recorder/table_managers/state_attributes.py b/homeassistant/components/recorder/table_managers/state_attributes.py index 3ae67b932bf..653ef1689bd 100644 --- a/homeassistant/components/recorder/table_managers/state_attributes.py +++ b/homeassistant/components/recorder/table_managers/state_attributes.py @@ -34,13 +34,10 @@ _LOGGER = logging.getLogger(__name__) class StateAttributesManager(BaseLRUTableManager[StateAttributes]): """Manage the StateAttributes table.""" - def __init__( - self, recorder: Recorder, exclude_attributes_by_domain: dict[str, set[str]] - ) -> None: + def __init__(self, recorder: Recorder) -> None: """Initialize the event type manager.""" super().__init__(recorder, CACHE_SIZE) self.active = True # always active - self._exclude_attributes_by_domain = exclude_attributes_by_domain self._entity_sources = entity_sources(recorder.hass) def serialize_from_event(self, event: Event) -> bytes | None: @@ -49,7 +46,6 @@ class StateAttributesManager(BaseLRUTableManager[StateAttributes]): return StateAttributes.shared_attrs_bytes_from_event( event, self._entity_sources, - self._exclude_attributes_by_domain, self.recorder.dialect_name, ) except JSON_ENCODE_EXCEPTIONS as ex: diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index e4e5e49eab5..0dfbb6005c4 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -119,7 +119,6 @@ def _default_recorder(hass): db_retry_wait=3, entity_filter=CONFIG_SCHEMA({DOMAIN: {}}), exclude_event_types=set(), - exclude_attributes_by_domain={}, ) @@ -2264,17 +2263,14 @@ async def test_connect_args_priority(hass: HomeAssistant, config_url) -> None: assert connect_params[0]["charset"] == "utf8mb4" -@pytest.mark.parametrize("core_state", [CoreState.starting, CoreState.running]) async def test_excluding_attributes_by_integration( recorder_mock: Recorder, hass: HomeAssistant, entity_registry: er.EntityRegistry, - core_state: CoreState, ) -> None: - """Test that an integration's recorder platform can exclude attributes.""" - hass.state = core_state + """Test that an entity can exclude attributes from being recorded.""" state = "restoring_from_db" - attributes = {"test_attr": 5, "excluded": 10} + attributes = {"test_attr": 5, "excluded_component": 10, "excluded_integration": 20} mock_platform( hass, "fake_integration.recorder", @@ -2284,10 +2280,17 @@ async def test_excluding_attributes_by_integration( hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "fake_integration"}) await hass.async_block_till_done() + class EntityWithExcludedAttributes(MockEntity): + _entity_component_unrecorded_attributes = frozenset({"excluded_component"}) + _unrecorded_attributes = frozenset({"excluded_integration"}) + entity_id = "test.fake_integration_recorder" - platform = MockEntityPlatform(hass, platform_name="fake_integration") - entity_platform = MockEntity(entity_id=entity_id, extra_state_attributes=attributes) - await platform.async_add_entities([entity_platform]) + entity_platform = MockEntityPlatform(hass, platform_name="fake_integration") + entity = EntityWithExcludedAttributes( + entity_id=entity_id, + extra_state_attributes=attributes, + ) + await entity_platform.async_add_entities([entity]) await hass.async_block_till_done() await async_wait_recording_done(hass) diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index c73a0db6c76..f5ea8ff1656 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -77,7 +77,7 @@ def test_from_event_to_db_state_attributes() -> None: dialect = SupportedDialect.MYSQL db_attrs.shared_attrs = StateAttributes.shared_attrs_bytes_from_event( - event, {}, {}, dialect + event, {}, dialect ) assert db_attrs.to_native() == attrs