Remove support for excluding attributes in recorder platforms (#100679)
parent
1b1901cb6d
commit
0dc21504f5
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue