From 4860a8d1e8853609c13fc4e7d0f962f9804276cf Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 3 May 2023 12:39:22 +0200 Subject: [PATCH] Don't use storage collection helper in ExposedEntities (#92396) * Don't use storage collection helper in ExposedEntities * Fix tests --- homeassistant/components/alexa/config.py | 3 +- homeassistant/components/alexa/handlers.py | 2 +- homeassistant/components/alexa/messages.py | 4 +- homeassistant/components/alexa/smart_home.py | 2 +- .../components/alexa/smart_home_http.py | 3 +- .../components/alexa/state_report.py | 2 +- .../components/cloud/alexa_config.py | 9 +- .../components/cloud/google_config.py | 28 +-- .../components/conversation/__init__.py | 6 +- .../components/conversation/default_agent.py | 17 +- .../components/google_assistant/helpers.py | 16 +- .../components/google_assistant/http.py | 2 +- .../google_assistant/report_state.py | 4 +- .../components/google_assistant/smart_home.py | 6 +- .../components/homeassistant/__init__.py | 2 +- .../homeassistant/exposed_entities.py | 194 ++++++++---------- .../components/switch_as_x/__init__.py | 2 +- .../components/switch_as_x/entity.py | 8 +- tests/components/alexa/test_smart_home.py | 21 +- tests/components/cloud/test_alexa_config.py | 42 ++-- tests/components/cloud/test_client.py | 6 +- tests/components/cloud/test_google_config.py | 40 ++-- tests/components/conversation/__init__.py | 6 +- .../conversation/test_default_agent.py | 2 +- tests/components/conversation/test_init.py | 6 +- tests/components/google_assistant/__init__.py | 2 +- .../google_assistant/test_helpers.py | 6 +- .../components/google_assistant/test_http.py | 9 +- .../google_assistant/test_smart_home.py | 2 +- .../homeassistant/test_exposed_entities.py | 87 ++++---- tests/components/switch_as_x/test_init.py | 8 +- 31 files changed, 248 insertions(+), 299 deletions(-) diff --git a/homeassistant/components/alexa/config.py b/homeassistant/components/alexa/config.py index f1c4ad729c6..cdbea2ca346 100644 --- a/homeassistant/components/alexa/config.py +++ b/homeassistant/components/alexa/config.py @@ -84,7 +84,8 @@ class AbstractConfig(ABC): unsub_func() self._unsub_proactive_report = None - async def should_expose(self, entity_id): + @callback + def should_expose(self, entity_id): """If an entity should be exposed.""" return False diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index ee9ef61787b..eb23b09627e 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -103,7 +103,7 @@ async def async_api_discovery( discovery_endpoints = [ alexa_entity.serialize_discovery() for alexa_entity in async_get_entities(hass, config) - if await config.should_expose(alexa_entity.entity_id) + if config.should_expose(alexa_entity.entity_id) ] return directive.response( diff --git a/homeassistant/components/alexa/messages.py b/homeassistant/components/alexa/messages.py index 7aa929abf2c..4dd154ea11f 100644 --- a/homeassistant/components/alexa/messages.py +++ b/homeassistant/components/alexa/messages.py @@ -30,7 +30,7 @@ class AlexaDirective: self.entity = self.entity_id = self.endpoint = self.instance = None - async def load_entity(self, hass, config): + def load_entity(self, hass, config): """Set attributes related to the entity for this request. Sets these attributes when self.has_endpoint is True: @@ -49,7 +49,7 @@ class AlexaDirective: self.entity_id = _endpoint_id.replace("#", ".") self.entity = hass.states.get(self.entity_id) - if not self.entity or not await config.should_expose(self.entity_id): + if not self.entity or not config.should_expose(self.entity_id): raise AlexaInvalidEndpointError(_endpoint_id) self.endpoint = ENTITY_ADAPTERS[self.entity.domain](hass, config, self.entity) diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index 6c2da5c01c1..24229507877 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -34,7 +34,7 @@ async def async_handle_message(hass, config, request, context=None, enabled=True await config.set_authorized(True) if directive.has_endpoint: - await directive.load_entity(hass, config) + directive.load_entity(hass, config) funct_ref = HANDLERS.get((directive.namespace, directive.name)) if funct_ref: diff --git a/homeassistant/components/alexa/smart_home_http.py b/homeassistant/components/alexa/smart_home_http.py index 5c7dd4d1402..3a702421d94 100644 --- a/homeassistant/components/alexa/smart_home_http.py +++ b/homeassistant/components/alexa/smart_home_http.py @@ -60,7 +60,8 @@ class AlexaConfig(AbstractConfig): """Return an identifier for the user that represents this config.""" return "" - async def should_expose(self, entity_id): + @core.callback + def should_expose(self, entity_id): """If an entity should be exposed.""" if not self._config[CONF_FILTER].empty_filter: return self._config[CONF_FILTER](entity_id) diff --git a/homeassistant/components/alexa/state_report.py b/homeassistant/components/alexa/state_report.py index b9e1426bbc1..a189c364c02 100644 --- a/homeassistant/components/alexa/state_report.py +++ b/homeassistant/components/alexa/state_report.py @@ -64,7 +64,7 @@ async def async_enable_proactive_mode(hass, smart_home_config): if new_state.domain not in ENTITY_ADAPTERS: return - if not await smart_home_config.should_expose(changed_entity): + if not smart_home_config.should_expose(changed_entity): _LOGGER.debug("Not exposing %s because filtered by config", changed_entity) return diff --git a/homeassistant/components/cloud/alexa_config.py b/homeassistant/components/cloud/alexa_config.py index 06d6589204b..212cbb26e0a 100644 --- a/homeassistant/components/cloud/alexa_config.py +++ b/homeassistant/components/cloud/alexa_config.py @@ -257,14 +257,15 @@ class CloudAlexaConfig(alexa_config.AbstractConfig): and entity_supported(self.hass, entity_id) ) - async def should_expose(self, entity_id): + @callback + def should_expose(self, entity_id): """If an entity should be exposed.""" if not self._config[CONF_FILTER].empty_filter: if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES: return False return self._config[CONF_FILTER](entity_id) - return await async_should_expose(self.hass, CLOUD_ALEXA, entity_id) + return async_should_expose(self.hass, CLOUD_ALEXA, entity_id) @callback def async_invalidate_access_token(self): @@ -423,7 +424,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig): is_enabled = self.enabled for entity in alexa_entities.async_get_entities(self.hass, self): - if is_enabled and await self.should_expose(entity.entity_id): + if is_enabled and self.should_expose(entity.entity_id): to_update.append(entity.entity_id) else: to_remove.append(entity.entity_id) @@ -482,7 +483,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig): entity_id = event.data["entity_id"] - if not await self.should_expose(entity_id): + if not self.should_expose(entity_id): return action = event.data["action"] diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index 4c8ebfbd9e9..a5700789112 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -222,9 +222,9 @@ class CloudGoogleConfig(AbstractConfig): self._handle_device_registry_updated, ) - async def should_expose(self, state): + def should_expose(self, state): """If a state object should be exposed.""" - return await self._should_expose_entity_id(state.entity_id) + return self._should_expose_entity_id(state.entity_id) def _should_expose_legacy(self, entity_id): """If an entity ID should be exposed.""" @@ -258,14 +258,14 @@ class CloudGoogleConfig(AbstractConfig): and _supported_legacy(self.hass, entity_id) ) - async def _should_expose_entity_id(self, entity_id): + def _should_expose_entity_id(self, entity_id): """If an entity should be exposed.""" if not self._config[CONF_FILTER].empty_filter: if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES: return False return self._config[CONF_FILTER](entity_id) - return await async_should_expose(self.hass, CLOUD_GOOGLE, entity_id) + return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id) @property def agent_user_id(self): @@ -358,7 +358,8 @@ class CloudGoogleConfig(AbstractConfig): """Handle updated preferences.""" self.async_schedule_google_sync_all() - async def _handle_entity_registry_updated(self, event: Event) -> None: + @callback + def _handle_entity_registry_updated(self, event: Event) -> None: """Handle when entity registry updated.""" if ( not self.enabled @@ -375,11 +376,12 @@ class CloudGoogleConfig(AbstractConfig): entity_id = event.data["entity_id"] - if not await self._should_expose_entity_id(entity_id): + if not self._should_expose_entity_id(entity_id): return self.async_schedule_google_sync_all() + @callback async def _handle_device_registry_updated(self, event: Event) -> None: """Handle when device registry updated.""" if ( @@ -394,15 +396,13 @@ class CloudGoogleConfig(AbstractConfig): return # Check if any exposed entity uses the device area - used = False - for entity_entry in er.async_entries_for_device( - er.async_get(self.hass), event.data["device_id"] + if not any( + entity_entry.area_id is None + and self._should_expose_entity_id(entity_entry.entity_id) + for entity_entry in er.async_entries_for_device( + er.async_get(self.hass), event.data["device_id"] + ) ): - if entity_entry.area_id is None and await self._should_expose_entity_id( - entity_entry.entity_id - ): - used = True - if not used: return self.async_schedule_google_sync_all() diff --git a/homeassistant/components/conversation/__init__.py b/homeassistant/components/conversation/__init__.py index b27a6ebee02..f156acfd568 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -94,7 +94,7 @@ CONFIG_SCHEMA = vol.Schema( def _get_agent_manager(hass: HomeAssistant) -> AgentManager: """Get the active agent.""" manager = AgentManager(hass) - hass.async_create_task(manager.async_setup()) + manager.async_setup() return manager @@ -393,9 +393,9 @@ class AgentManager: self._agents: dict[str, AbstractConversationAgent] = {} self._builtin_agent_init_lock = asyncio.Lock() - async def async_setup(self) -> None: + def async_setup(self) -> None: """Set up the conversation agents.""" - await async_setup_default_agent(self.hass) + async_setup_default_agent(self.hass) async def async_get_agent( self, agent_id: str | None = None diff --git a/homeassistant/components/conversation/default_agent.py b/homeassistant/components/conversation/default_agent.py index d57de76f5e0..d347140af2e 100644 --- a/homeassistant/components/conversation/default_agent.py +++ b/homeassistant/components/conversation/default_agent.py @@ -73,20 +73,23 @@ def _get_language_variations(language: str) -> Iterable[str]: yield lang -async def async_setup(hass: core.HomeAssistant) -> None: +@core.callback +def async_setup(hass: core.HomeAssistant) -> None: """Set up entity registry listener for the default agent.""" entity_registry = er.async_get(hass) for entity_id in entity_registry.entities: - await async_should_expose(hass, DOMAIN, entity_id) + async_should_expose(hass, DOMAIN, entity_id) - async def async_handle_entity_registry_changed(event: core.Event) -> None: + @core.callback + def async_handle_entity_registry_changed(event: core.Event) -> None: """Set expose flag on newly created entities.""" if event.data["action"] == "create": - await async_should_expose(hass, DOMAIN, event.data["entity_id"]) + async_should_expose(hass, DOMAIN, event.data["entity_id"]) hass.bus.async_listen( er.EVENT_ENTITY_REGISTRY_UPDATED, async_handle_entity_registry_changed, + run_immediately=True, ) @@ -154,7 +157,7 @@ class DefaultAgent(AbstractConversationAgent): conversation_id, ) - slot_lists = await self._make_slot_lists() + slot_lists = self._make_slot_lists() result = await self.hass.async_add_executor_job( self._recognize, @@ -483,7 +486,7 @@ class DefaultAgent(AbstractConversationAgent): """Handle updated preferences.""" self._slot_lists = None - async def _make_slot_lists(self) -> dict[str, SlotList]: + def _make_slot_lists(self) -> dict[str, SlotList]: """Create slot lists with areas and entity names/aliases.""" if self._slot_lists is not None: return self._slot_lists @@ -493,7 +496,7 @@ class DefaultAgent(AbstractConversationAgent): entities = [ entity for entity in entity_registry.entities.values() - if await async_should_expose(self.hass, DOMAIN, entity.entity_id) + if async_should_expose(self.hass, DOMAIN, entity.entity_id) ] devices = dr.async_get(self.hass) diff --git a/homeassistant/components/google_assistant/helpers.py b/homeassistant/components/google_assistant/helpers.py index d192b2514de..e194242df91 100644 --- a/homeassistant/components/google_assistant/helpers.py +++ b/homeassistant/components/google_assistant/helpers.py @@ -175,7 +175,7 @@ class AbstractConfig(ABC): """Get agent user ID from context.""" @abstractmethod - async def should_expose(self, state) -> bool: + def should_expose(self, state) -> bool: """Return if entity should be exposed.""" def should_2fa(self, state): @@ -535,14 +535,16 @@ class GoogleEntity: ] return self._traits - async def should_expose(self): + @callback + def should_expose(self): """If entity should be exposed.""" - return await self.config.should_expose(self.state) + return self.config.should_expose(self.state) - async def should_expose_local(self) -> bool: + @callback + def should_expose_local(self) -> bool: """Return if the entity should be exposed locally.""" return ( - await self.should_expose() + self.should_expose() and get_google_type( self.state.domain, self.state.attributes.get(ATTR_DEVICE_CLASS) ) @@ -585,7 +587,7 @@ class GoogleEntity: trait.might_2fa(domain, features, device_class) for trait in self.traits() ) - async def sync_serialize(self, agent_user_id, instance_uuid): + def sync_serialize(self, agent_user_id, instance_uuid): """Serialize entity for a SYNC response. https://developers.google.com/actions/smarthome/create-app#actiondevicessync @@ -621,7 +623,7 @@ class GoogleEntity: device["name"]["nicknames"].extend(entity_entry.aliases) # Add local SDK info if enabled - if self.config.is_local_sdk_active and await self.should_expose_local(): + if self.config.is_local_sdk_active and self.should_expose_local(): device["otherDeviceIds"] = [{"deviceId": self.entity_id}] device["customData"] = { "webhookId": self.config.get_local_webhook_id(agent_user_id), diff --git a/homeassistant/components/google_assistant/http.py b/homeassistant/components/google_assistant/http.py index 4aadc9c4002..84d5e4a3364 100644 --- a/homeassistant/components/google_assistant/http.py +++ b/homeassistant/components/google_assistant/http.py @@ -111,7 +111,7 @@ class GoogleConfig(AbstractConfig): """Return if states should be proactively reported.""" return self._config.get(CONF_REPORT_STATE) - async def should_expose(self, state) -> bool: + def should_expose(self, state) -> bool: """Return if entity should be exposed.""" expose_by_default = self._config.get(CONF_EXPOSE_BY_DEFAULT) exposed_domains = self._config.get(CONF_EXPOSED_DOMAINS) diff --git a/homeassistant/components/google_assistant/report_state.py b/homeassistant/components/google_assistant/report_state.py index c0a65cbfa7a..737b54c8b1e 100644 --- a/homeassistant/components/google_assistant/report_state.py +++ b/homeassistant/components/google_assistant/report_state.py @@ -63,7 +63,7 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig if not new_state: return - if not await google_config.should_expose(new_state): + if not google_config.should_expose(new_state): return entity = GoogleEntity(hass, google_config, new_state) @@ -115,7 +115,7 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig checker = await create_checker(hass, DOMAIN, extra_significant_check) for entity in async_get_entities(hass, google_config): - if not await entity.should_expose(): + if not entity.should_expose(): continue try: diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index 798743a447d..1b1b443baac 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -87,11 +87,11 @@ async def async_devices_sync_response(hass, config, agent_user_id): devices = [] for entity in entities: - if not await entity.should_expose(): + if not entity.should_expose(): continue try: - devices.append(await entity.sync_serialize(agent_user_id, instance_uuid)) + devices.append(entity.sync_serialize(agent_user_id, instance_uuid)) except Exception: # pylint: disable=broad-except _LOGGER.exception("Error serializing %s", entity.entity_id) @@ -318,7 +318,7 @@ async def async_devices_reachable( "devices": [ entity.reachable_device_serialize() for entity in async_get_entities(hass, data.config) - if entity.entity_id in google_ids and await entity.should_expose_local() + if entity.entity_id in google_ids and entity.should_expose_local() ] } diff --git a/homeassistant/components/homeassistant/__init__.py b/homeassistant/components/homeassistant/__init__.py index 45646b72b7f..987a4317ba8 100644 --- a/homeassistant/components/homeassistant/__init__.py +++ b/homeassistant/components/homeassistant/__init__.py @@ -343,7 +343,7 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no ) exposed_entities = ExposedEntities(hass) - await exposed_entities.async_load() + await exposed_entities.async_initialize() hass.data[DATA_EXPOSED_ENTITIES] = exposed_entities return True diff --git a/homeassistant/components/homeassistant/exposed_entities.py b/homeassistant/components/homeassistant/exposed_entities.py index 81b3a60b3f5..56df611b323 100644 --- a/homeassistant/components/homeassistant/exposed_entities.py +++ b/homeassistant/components/homeassistant/exposed_entities.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable, Mapping import dataclasses from itertools import chain -from typing import Any +from typing import Any, TypedDict import voluptuous as vol @@ -15,11 +15,6 @@ from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.collection import ( - IDManager, - SerializedStorageCollection, - StorageCollection, -) from homeassistant.helpers.entity import get_device_class from homeassistant.helpers.storage import Store @@ -89,30 +84,21 @@ class ExposedEntity: assistants: dict[str, dict[str, Any]] - def to_json(self, entity_id: str) -> dict[str, Any]: + def to_json(self) -> dict[str, Any]: """Return a JSON serializable representation for storage.""" return { "assistants": self.assistants, - "id": entity_id, } -class SerializedExposedEntities(SerializedStorageCollection): +class SerializedExposedEntities(TypedDict): """Serialized exposed entities storage storage collection.""" assistants: dict[str, dict[str, Any]] + exposed_entities: dict[str, dict[str, Any]] -class ExposedEntitiesIDManager(IDManager): - """ID manager for tags.""" - - def generate_id(self, suggestion: str) -> str: - """Generate an ID.""" - assert not self.has_id(suggestion) - return suggestion - - -class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities]): +class ExposedEntities: """Control assistant settings. Settings for entities without a unique_id are stored in the store. @@ -120,21 +106,23 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities """ _assistants: dict[str, AssistantPreferences] + entities: dict[str, ExposedEntity] def __init__(self, hass: HomeAssistant) -> None: """Initialize.""" - super().__init__( - Store(hass, STORAGE_VERSION, STORAGE_KEY), ExposedEntitiesIDManager() - ) + self._hass = hass self._listeners: dict[str, list[Callable[[], None]]] = {} + self._store: Store[SerializedExposedEntities] = Store( + hass, STORAGE_VERSION, STORAGE_KEY + ) - async def async_load(self) -> None: + async def async_initialize(self) -> None: """Finish initializing.""" - await super().async_load() - websocket_api.async_register_command(self.hass, ws_expose_entity) - websocket_api.async_register_command(self.hass, ws_expose_new_entities_get) - websocket_api.async_register_command(self.hass, ws_expose_new_entities_set) - websocket_api.async_register_command(self.hass, ws_list_exposed_entities) + websocket_api.async_register_command(self._hass, ws_expose_entity) + websocket_api.async_register_command(self._hass, ws_expose_new_entities_get) + websocket_api.async_register_command(self._hass, ws_expose_new_entities_set) + websocket_api.async_register_command(self._hass, ws_list_exposed_entities) + await self._async_load_data() @callback def async_listen_entity_updates( @@ -143,18 +131,17 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities """Listen for updates to entity expose settings.""" self._listeners.setdefault(assistant, []).append(listener) - async def async_expose_entity( + @callback + def async_expose_entity( self, assistant: str, entity_id: str, should_expose: bool ) -> None: """Expose an entity to an assistant. Notify listeners if expose flag was changed. """ - entity_registry = er.async_get(self.hass) + entity_registry = er.async_get(self._hass) if not (registry_entry := entity_registry.async_get(entity_id)): - return await self._async_expose_legacy_entity( - assistant, entity_id, should_expose - ) + return self._async_expose_legacy_entity(assistant, entity_id, should_expose) assistant_options: Mapping[str, Any] if ( @@ -169,7 +156,7 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities for listener in self._listeners.get(assistant, []): listener() - async def _async_expose_legacy_entity( + def _async_expose_legacy_entity( self, assistant: str, entity_id: str, should_expose: bool ) -> None: """Expose an entity to an assistant. @@ -177,23 +164,20 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities Notify listeners if expose flag was changed. """ if ( - (exposed_entity := self.data.get(entity_id)) + (exposed_entity := self.entities.get(entity_id)) and (assistant_options := exposed_entity.assistants.get(assistant, {})) and assistant_options.get("should_expose") == should_expose ): return if exposed_entity: - await self.async_update_item( - entity_id, {"assistants": {assistant: {"should_expose": should_expose}}} + new_exposed_entity = self._update_exposed_entity( + assistant, entity_id, should_expose ) else: - await self.async_create_item( - { - "entity_id": entity_id, - "assistants": {assistant: {"should_expose": should_expose}}, - } - ) + new_exposed_entity = self._new_exposed_entity(assistant, should_expose) + self.entities[entity_id] = new_exposed_entity + self._async_schedule_save() for listener in self._listeners.get(assistant, []): listener() @@ -215,11 +199,11 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities self, assistant: str ) -> dict[str, Mapping[str, Any]]: """Get all entity expose settings for an assistant.""" - entity_registry = er.async_get(self.hass) + entity_registry = er.async_get(self._hass) result: dict[str, Mapping[str, Any]] = {} options: Mapping | None - for entity_id, exposed_entity in self.data.items(): + for entity_id, exposed_entity in self.entities.items(): if options := exposed_entity.assistants.get(assistant): result[entity_id] = options @@ -232,13 +216,13 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities @callback def async_get_entity_settings(self, entity_id: str) -> dict[str, Mapping[str, Any]]: """Get assistant expose settings for an entity.""" - entity_registry = er.async_get(self.hass) + entity_registry = er.async_get(self._hass) result: dict[str, Mapping[str, Any]] = {} assistant_settings: Mapping if registry_entry := entity_registry.async_get(entity_id): assistant_settings = registry_entry.options - elif exposed_entity := self.data.get(entity_id): + elif exposed_entity := self.entities.get(entity_id): assistant_settings = exposed_entity.assistants else: raise HomeAssistantError("Unknown entity") @@ -249,16 +233,17 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities return result - async def async_should_expose(self, assistant: str, entity_id: str) -> bool: + @callback + def async_should_expose(self, assistant: str, entity_id: str) -> bool: """Return True if an entity should be exposed to an assistant.""" should_expose: bool if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES: return False - entity_registry = er.async_get(self.hass) + entity_registry = er.async_get(self._hass) if not (registry_entry := entity_registry.async_get(entity_id)): - return await self._async_should_expose_legacy_entity(assistant, entity_id) + return self._async_should_expose_legacy_entity(assistant, entity_id) if assistant in registry_entry.options: if "should_expose" in registry_entry.options[assistant]: should_expose = registry_entry.options[assistant]["should_expose"] @@ -277,14 +262,14 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities return should_expose - async def _async_should_expose_legacy_entity( + def _async_should_expose_legacy_entity( self, assistant: str, entity_id: str ) -> bool: """Return True if an entity should be exposed to an assistant.""" should_expose: bool if ( - exposed_entity := self.data.get(entity_id) + exposed_entity := self.entities.get(entity_id) ) and assistant in exposed_entity.assistants: if "should_expose" in exposed_entity.assistants[assistant]: should_expose = exposed_entity.assistants[assistant]["should_expose"] @@ -296,16 +281,13 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities should_expose = False if exposed_entity: - await self.async_update_item( - entity_id, {"assistants": {assistant: {"should_expose": should_expose}}} + new_exposed_entity = self._update_exposed_entity( + assistant, entity_id, should_expose ) else: - await self.async_create_item( - { - "entity_id": entity_id, - "assistants": {assistant: {"should_expose": should_expose}}, - } - ) + new_exposed_entity = self._new_exposed_entity(assistant, should_expose) + self.entities[entity_id] = new_exposed_entity + self._async_schedule_save() return should_expose @@ -323,7 +305,7 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities if domain in DEFAULT_EXPOSED_DOMAINS: return True - device_class = get_device_class(self.hass, entity_id) + device_class = get_device_class(self._hass, entity_id) if ( domain == "binary_sensor" and device_class in DEFAULT_EXPOSED_BINARY_SENSOR_DEVICE_CLASSES @@ -335,73 +317,66 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities return False - async def _process_create_data(self, data: dict) -> dict: - """Validate the config is valid.""" - return data - - @callback - def _get_suggested_id(self, info: dict) -> str: - """Suggest an ID based on the config.""" - entity_id: str = info["entity_id"] - return entity_id - - async def _update_data( - self, item: ExposedEntity, update_data: dict + def _update_exposed_entity( + self, + assistant: str, + entity_id: str, + should_expose: bool, ) -> ExposedEntity: - """Return a new updated item.""" - new_assistant_settings: dict[str, Any] = update_data["assistants"] - old_assistant_settings = item.assistants - for assistant, old_settings in old_assistant_settings.items(): - new_settings = new_assistant_settings.get(assistant, {}) - new_assistant_settings[assistant] = old_settings | new_settings - return dataclasses.replace(item, assistants=new_assistant_settings) + """Update an exposed entity.""" + entity = self.entities[entity_id] + assistants = dict(entity.assistants) + old_settings = assistants.get(assistant, {}) + assistants[assistant] = old_settings | {"should_expose": should_expose} + return ExposedEntity(assistants) - def _create_item(self, item_id: str, data: dict) -> ExposedEntity: - """Create an item from validated config.""" + def _new_exposed_entity(self, assistant: str, should_expose: bool) -> ExposedEntity: + """Create a new exposed entity.""" return ExposedEntity( - assistants=data["assistants"], + assistants={assistant: {"should_expose": should_expose}}, ) - def _deserialize_item(self, data: dict) -> ExposedEntity: - """Create an item from its serialized representation.""" - return ExposedEntity( - assistants=data["assistants"], - ) - - def _serialize_item(self, item_id: str, item: ExposedEntity) -> dict: - """Return the serialized representation of an item for storing.""" - return item.to_json(item_id) - async def _async_load_data(self) -> SerializedExposedEntities | None: """Load from the store.""" - data = await super()._async_load_data() + data = await self._store.async_load() assistants: dict[str, AssistantPreferences] = {} + exposed_entities: dict[str, ExposedEntity] = {} - if data and "assistants" in data: + if data: for domain, preferences in data["assistants"].items(): assistants[domain] = AssistantPreferences(**preferences) - self._assistants = assistants + if data and "exposed_entities" in data: + for entity_id, preferences in data["exposed_entities"].items(): + exposed_entities[entity_id] = ExposedEntity(**preferences) - if data and "items" not in data: - return None # type: ignore[unreachable] + self._assistants = assistants + self.entities = exposed_entities return data + @callback + def _async_schedule_save(self) -> None: + """Schedule saving the preferences.""" + self._store.async_delay_save(self._data_to_save, SAVE_DELAY) + @callback def _data_to_save(self) -> SerializedExposedEntities: """Return JSON-compatible date for storing to file.""" - base_data = super()._base_data_to_save() return { - "items": base_data["items"], "assistants": { domain: preferences.to_json() for domain, preferences in self._assistants.items() }, + "exposed_entities": { + entity_id: entity.to_json() + for entity_id, entity in self.entities.items() + }, } +@callback @websocket_api.require_admin @websocket_api.websocket_command( { @@ -411,8 +386,7 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities vol.Required("should_expose"): bool, } ) -@websocket_api.async_response -async def ws_expose_entity( +def ws_expose_entity( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] ) -> None: """Expose an entity to an assistant.""" @@ -434,7 +408,7 @@ async def ws_expose_entity( exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] for entity_id in entity_ids: for assistant in msg["assistants"]: - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( assistant, entity_id, msg["should_expose"] ) connection.send_result(msg["id"]) @@ -455,7 +429,7 @@ def ws_list_exposed_entities( exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] entity_registry = er.async_get(hass) - for entity_id in chain(exposed_entities.data, entity_registry.entities): + for entity_id in chain(exposed_entities.entities, entity_registry.entities): result[entity_id] = {} entity_settings = async_get_entity_settings(hass, entity_id) for assistant, settings in entity_settings.items(): @@ -527,7 +501,8 @@ def async_get_entity_settings( return exposed_entities.async_get_entity_settings(entity_id) -async def async_expose_entity( +@callback +def async_expose_entity( hass: HomeAssistant, assistant: str, entity_id: str, @@ -535,12 +510,11 @@ async def async_expose_entity( ) -> None: """Get assistant expose settings for an entity.""" exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - await exposed_entities.async_expose_entity(assistant, entity_id, should_expose) + exposed_entities.async_expose_entity(assistant, entity_id, should_expose) -async def async_should_expose( - hass: HomeAssistant, assistant: str, entity_id: str -) -> bool: +@callback +def async_should_expose(hass: HomeAssistant, assistant: str, entity_id: str) -> bool: """Return True if an entity should be exposed to an assistant.""" exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - return await exposed_entities.async_should_expose(assistant, entity_id) + return exposed_entities.async_should_expose(assistant, entity_id) diff --git a/homeassistant/components/switch_as_x/__init__.py b/homeassistant/components/switch_as_x/__init__.py index 6e6dffd2337..ef64a86c6e8 100644 --- a/homeassistant/components/switch_as_x/__init__.py +++ b/homeassistant/components/switch_as_x/__init__.py @@ -138,6 +138,6 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: for assistant, settings in expose_settings.items(): if (should_expose := settings.get("should_expose")) is None: continue - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( hass, assistant, switch_entity_id, should_expose ) diff --git a/homeassistant/components/switch_as_x/entity.py b/homeassistant/components/switch_as_x/entity.py index d2e3995b85e..a73271bdc83 100644 --- a/homeassistant/components/switch_as_x/entity.py +++ b/homeassistant/components/switch_as_x/entity.py @@ -111,7 +111,7 @@ class BaseEntity(Entity): return registry.async_update_entity(self.entity_id, name=wrapped_switch.name) - async def copy_expose_settings() -> None: + def copy_expose_settings() -> None: """Copy assistant expose settings from the wrapped entity. Also unexpose the wrapped entity if exposed. @@ -122,15 +122,15 @@ class BaseEntity(Entity): for assistant, settings in expose_settings.items(): if (should_expose := settings.get("should_expose")) is None: continue - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( self.hass, assistant, self.entity_id, should_expose ) - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( self.hass, assistant, self._switch_entity_id, False ) copy_custom_name(wrapped_switch) - await copy_expose_settings() + copy_expose_settings() class BaseToggleEntity(BaseEntity, ToggleEntity): diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index 36cc005bf2f..601f59fd118 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -2450,18 +2450,13 @@ async def test_exclude_filters(hass: HomeAssistant) -> None: hass.states.async_set("cover.deny", "off", {"friendly_name": "Blocked cover"}) alexa_config = MockConfig(hass) - filter = entityfilter.generate_filter( + alexa_config.should_expose = entityfilter.generate_filter( include_domains=[], include_entities=[], exclude_domains=["script"], exclude_entities=["cover.deny"], ) - async def mock_should_expose(entity_id): - return filter(entity_id) - - alexa_config.should_expose = mock_should_expose - msg = await smart_home.async_handle_message(hass, alexa_config, request) await hass.async_block_till_done() @@ -2486,18 +2481,13 @@ async def test_include_filters(hass: HomeAssistant) -> None: hass.states.async_set("group.allow", "off", {"friendly_name": "Allowed group"}) alexa_config = MockConfig(hass) - filter = entityfilter.generate_filter( + alexa_config.should_expose = entityfilter.generate_filter( include_domains=["automation", "group"], include_entities=["script.deny"], exclude_domains=[], exclude_entities=[], ) - async def mock_should_expose(entity_id): - return filter(entity_id) - - alexa_config.should_expose = mock_should_expose - msg = await smart_home.async_handle_message(hass, alexa_config, request) await hass.async_block_till_done() @@ -2516,18 +2506,13 @@ async def test_never_exposed_entities(hass: HomeAssistant) -> None: hass.states.async_set("group.allow", "off", {"friendly_name": "Allowed group"}) alexa_config = MockConfig(hass) - filter = entityfilter.generate_filter( + alexa_config.should_expose = entityfilter.generate_filter( include_domains=["group"], include_entities=[], exclude_domains=[], exclude_entities=[], ) - async def mock_should_expose(entity_id): - return filter(entity_id) - - alexa_config.should_expose = mock_should_expose - msg = await smart_home.async_handle_message(hass, alexa_config, request) await hass.async_block_till_done() diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index bf4890e92dd..257d04cc697 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -38,10 +38,10 @@ def expose_new(hass, expose_new): exposed_entities.async_set_expose_new_entities("cloud.alexa", expose_new) -async def expose_entity(hass, entity_id, should_expose): +def expose_entity(hass, entity_id, should_expose): """Expose an entity to Alexa.""" exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - await exposed_entities.async_expose_entity("cloud.alexa", entity_id, should_expose) + exposed_entities.async_expose_entity("cloud.alexa", entity_id, should_expose) async def test_alexa_config_expose_entity_prefs( @@ -95,35 +95,35 @@ async def test_alexa_config_expose_entity_prefs( alexa_report_state=False, ) expose_new(hass, True) - await expose_entity(hass, entity_entry5.entity_id, False) + expose_entity(hass, entity_entry5.entity_id, False) conf = alexa_config.CloudAlexaConfig( hass, ALEXA_SCHEMA({}), "mock-user-id", cloud_prefs, cloud_stub ) await conf.async_initialize() # an entity which is not in the entity registry can be exposed - await expose_entity(hass, "light.kitchen", True) - assert await conf.should_expose("light.kitchen") + expose_entity(hass, "light.kitchen", True) + assert conf.should_expose("light.kitchen") # categorized and hidden entities should not be exposed - assert not await conf.should_expose(entity_entry1.entity_id) - assert not await conf.should_expose(entity_entry2.entity_id) - assert not await conf.should_expose(entity_entry3.entity_id) - assert not await conf.should_expose(entity_entry4.entity_id) + assert not conf.should_expose(entity_entry1.entity_id) + assert not conf.should_expose(entity_entry2.entity_id) + assert not conf.should_expose(entity_entry3.entity_id) + assert not conf.should_expose(entity_entry4.entity_id) # this has been hidden - assert not await conf.should_expose(entity_entry5.entity_id) + assert not conf.should_expose(entity_entry5.entity_id) # exposed by default - assert await conf.should_expose(entity_entry6.entity_id) + assert conf.should_expose(entity_entry6.entity_id) - await expose_entity(hass, entity_entry5.entity_id, True) - assert await conf.should_expose(entity_entry5.entity_id) + expose_entity(hass, entity_entry5.entity_id, True) + assert conf.should_expose(entity_entry5.entity_id) - await expose_entity(hass, entity_entry5.entity_id, None) - assert not await conf.should_expose(entity_entry5.entity_id) + expose_entity(hass, entity_entry5.entity_id, None) + assert not conf.should_expose(entity_entry5.entity_id) assert "alexa" not in hass.config.components await hass.async_block_till_done() assert "alexa" in hass.config.components - assert not await conf.should_expose(entity_entry5.entity_id) + assert not conf.should_expose(entity_entry5.entity_id) async def test_alexa_config_report_state( @@ -368,7 +368,7 @@ async def test_alexa_update_expose_trigger_sync( await conf.async_initialize() with patch_sync_helper() as (to_update, to_remove): - await expose_entity(hass, light_entry.entity_id, True) + expose_entity(hass, light_entry.entity_id, True) await hass.async_block_till_done() async_fire_time_changed(hass, fire_all=True) await hass.async_block_till_done() @@ -378,9 +378,9 @@ async def test_alexa_update_expose_trigger_sync( assert to_remove == [] with patch_sync_helper() as (to_update, to_remove): - await expose_entity(hass, light_entry.entity_id, False) - await expose_entity(hass, binary_sensor_entry.entity_id, True) - await expose_entity(hass, sensor_entry.entity_id, True) + expose_entity(hass, light_entry.entity_id, False) + expose_entity(hass, binary_sensor_entry.entity_id, True) + expose_entity(hass, sensor_entry.entity_id, True) await hass.async_block_till_done() async_fire_time_changed(hass, fire_all=True) await hass.async_block_till_done() @@ -586,7 +586,7 @@ async def test_alexa_config_migrate_expose_entity_prefs( alexa_report_state=False, alexa_settings_version=1, ) - await expose_entity(hass, entity_migrated.entity_id, False) + expose_entity(hass, entity_migrated.entity_id, False) cloud_prefs._prefs[PREF_ALEXA_ENTITY_CONFIGS]["light.unknown"] = { PREF_SHOULD_EXPOSE: True diff --git a/tests/components/cloud/test_client.py b/tests/components/cloud/test_client.py index 9bca4b79340..d1e1a8ce112 100644 --- a/tests/components/cloud/test_client.py +++ b/tests/components/cloud/test_client.py @@ -265,13 +265,13 @@ async def test_google_config_expose_entity( state = State(entity_entry.entity_id, "on") gconf = await cloud_client.get_google_config() - assert await gconf.should_expose(state) + assert gconf.should_expose(state) - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( "cloud.google_assistant", entity_entry.entity_id, False ) - assert not await gconf.should_expose(state) + assert not gconf.should_expose(state) async def test_google_config_should_2fa( diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 51e8de98301..877a6efaf05 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -46,10 +46,10 @@ def expose_new(hass, expose_new): exposed_entities.async_set_expose_new_entities("cloud.google_assistant", expose_new) -async def expose_entity(hass, entity_id, should_expose): +def expose_entity(hass, entity_id, should_expose): """Expose an entity to Google.""" exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( "cloud.google_assistant", entity_id, should_expose ) @@ -150,7 +150,7 @@ async def test_google_update_expose_trigger_sync( with patch.object(config, "async_sync_entities") as mock_sync, patch.object( ga_helpers, "SYNC_DELAY", 0 ): - await expose_entity(hass, light_entry.entity_id, True) + expose_entity(hass, light_entry.entity_id, True) await hass.async_block_till_done() async_fire_time_changed(hass, utcnow()) await hass.async_block_till_done() @@ -160,9 +160,9 @@ async def test_google_update_expose_trigger_sync( with patch.object(config, "async_sync_entities") as mock_sync, patch.object( ga_helpers, "SYNC_DELAY", 0 ): - await expose_entity(hass, light_entry.entity_id, False) - await expose_entity(hass, binary_sensor_entry.entity_id, True) - await expose_entity(hass, sensor_entry.entity_id, True) + expose_entity(hass, light_entry.entity_id, False) + expose_entity(hass, binary_sensor_entry.entity_id, True) + expose_entity(hass, sensor_entry.entity_id, True) await hass.async_block_till_done() async_fire_time_changed(hass, utcnow()) await hass.async_block_till_done() @@ -384,7 +384,7 @@ async def test_google_config_expose_entity_prefs( ) expose_new(hass, True) - await expose_entity(hass, entity_entry5.entity_id, False) + expose_entity(hass, entity_entry5.entity_id, False) state = State("light.kitchen", "on") state_config = State(entity_entry1.entity_id, "on") @@ -395,23 +395,23 @@ async def test_google_config_expose_entity_prefs( state_exposed_default = State(entity_entry6.entity_id, "on") # an entity which is not in the entity registry can be exposed - await expose_entity(hass, "light.kitchen", True) - assert await mock_conf.should_expose(state) + expose_entity(hass, "light.kitchen", True) + assert mock_conf.should_expose(state) # categorized and hidden entities should not be exposed - assert not await mock_conf.should_expose(state_config) - assert not await mock_conf.should_expose(state_diagnostic) - assert not await mock_conf.should_expose(state_hidden_integration) - assert not await mock_conf.should_expose(state_hidden_user) + assert not mock_conf.should_expose(state_config) + assert not mock_conf.should_expose(state_diagnostic) + assert not mock_conf.should_expose(state_hidden_integration) + assert not mock_conf.should_expose(state_hidden_user) # this has been hidden - assert not await mock_conf.should_expose(state_not_exposed) + assert not mock_conf.should_expose(state_not_exposed) # exposed by default - assert await mock_conf.should_expose(state_exposed_default) + assert mock_conf.should_expose(state_exposed_default) - await expose_entity(hass, entity_entry5.entity_id, True) - assert await mock_conf.should_expose(state_not_exposed) + expose_entity(hass, entity_entry5.entity_id, True) + assert mock_conf.should_expose(state_not_exposed) - await expose_entity(hass, entity_entry5.entity_id, None) - assert not await mock_conf.should_expose(state_not_exposed) + expose_entity(hass, entity_entry5.entity_id, None) + assert not mock_conf.should_expose(state_not_exposed) def test_enabled_requires_valid_sub( @@ -535,7 +535,7 @@ async def test_google_config_migrate_expose_entity_prefs( google_report_state=False, google_settings_version=1, ) - await expose_entity(hass, entity_migrated.entity_id, False) + expose_entity(hass, entity_migrated.entity_id, False) cloud_prefs._prefs[PREF_GOOGLE_ENTITY_CONFIGS]["light.unknown"] = { PREF_SHOULD_EXPOSE: True diff --git a/tests/components/conversation/__init__.py b/tests/components/conversation/__init__.py index fb455da945b..6eadb068054 100644 --- a/tests/components/conversation/__init__.py +++ b/tests/components/conversation/__init__.py @@ -51,9 +51,7 @@ def expose_new(hass, expose_new): exposed_entities.async_set_expose_new_entities(conversation.DOMAIN, expose_new) -async def expose_entity(hass, entity_id, should_expose): +def expose_entity(hass, entity_id, should_expose): """Expose an entity to the default agent.""" exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - await exposed_entities.async_expose_entity( - conversation.DOMAIN, entity_id, should_expose - ) + exposed_entities.async_expose_entity(conversation.DOMAIN, entity_id, should_expose) diff --git a/tests/components/conversation/test_default_agent.py b/tests/components/conversation/test_default_agent.py index daba360f7bf..44bb3111987 100644 --- a/tests/components/conversation/test_default_agent.py +++ b/tests/components/conversation/test_default_agent.py @@ -108,7 +108,7 @@ async def test_exposed_areas( hass.states.async_set(bedroom_light.entity_id, "on") # Hide the bedroom light - await expose_entity(hass, bedroom_light.entity_id, False) + expose_entity(hass, bedroom_light.entity_id, False) result = await conversation.async_converse( hass, "turn on lights in the kitchen", None, Context(), None diff --git a/tests/components/conversation/test_init.py b/tests/components/conversation/test_init.py index b6bb4aad68f..6b981cbbdfa 100644 --- a/tests/components/conversation/test_init.py +++ b/tests/components/conversation/test_init.py @@ -680,7 +680,7 @@ async def test_http_processing_intent_entity_exposed( } # Unexpose the entity - await expose_entity(hass, "light.kitchen", False) + expose_entity(hass, "light.kitchen", False) await hass.async_block_till_done() client = await hass_client() @@ -730,7 +730,7 @@ async def test_http_processing_intent_entity_exposed( } # Now expose the entity - await expose_entity(hass, "light.kitchen", True) + expose_entity(hass, "light.kitchen", True) await hass.async_block_till_done() client = await hass_client() @@ -845,7 +845,7 @@ async def test_http_processing_intent_conversion_not_expose_new( } # Expose the entity - await expose_entity(hass, "light.kitchen", True) + expose_entity(hass, "light.kitchen", True) await hass.async_block_till_done() resp = await client.post( diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index 29ad5e6710a..2122818bbb4 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -58,7 +58,7 @@ class MockConfig(helpers.AbstractConfig): """Get agent user ID making request.""" return context.user_id - async def should_expose(self, state): + def should_expose(self, state): """Expose it all.""" return self._should_expose is None or self._should_expose(state) diff --git a/tests/components/google_assistant/test_helpers.py b/tests/components/google_assistant/test_helpers.py index cda3ded25be..793db076c79 100644 --- a/tests/components/google_assistant/test_helpers.py +++ b/tests/components/google_assistant/test_helpers.py @@ -49,13 +49,13 @@ async def test_google_entity_sync_serialize_with_local_sdk(hass: HomeAssistant) ) entity = helpers.GoogleEntity(hass, config, hass.states.get("light.ceiling_lights")) - serialized = await entity.sync_serialize(None, "mock-uuid") + serialized = entity.sync_serialize(None, "mock-uuid") assert "otherDeviceIds" not in serialized assert "customData" not in serialized config.async_enable_local_sdk() - serialized = await entity.sync_serialize("mock-user-id", "abcdef") + serialized = entity.sync_serialize("mock-user-id", "abcdef") assert serialized["otherDeviceIds"] == [{"deviceId": "light.ceiling_lights"}] assert serialized["customData"] == { "httpPort": 1234, @@ -68,7 +68,7 @@ async def test_google_entity_sync_serialize_with_local_sdk(hass: HomeAssistant) "homeassistant.components.google_assistant.helpers.get_google_type", return_value=device_type, ): - serialized = await entity.sync_serialize(None, "mock-uuid") + serialized = entity.sync_serialize(None, "mock-uuid") assert "otherDeviceIds" not in serialized assert "customData" not in serialized diff --git a/tests/components/google_assistant/test_http.py b/tests/components/google_assistant/test_http.py index 5297d6b29e5..b7dc880ede0 100644 --- a/tests/components/google_assistant/test_http.py +++ b/tests/components/google_assistant/test_http.py @@ -257,9 +257,7 @@ async def test_should_expose(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert ( - await config.should_expose( - State(DOMAIN + ".mock", "mock", {"view": "not None"}) - ) + config.should_expose(State(DOMAIN + ".mock", "mock", {"view": "not None"})) is False ) @@ -267,10 +265,7 @@ async def test_should_expose(hass: HomeAssistant) -> None: # Wait for google_assistant.helpers.async_initialize.sync_google to be called await hass.async_block_till_done() - assert ( - await config.should_expose(State(CLOUD_NEVER_EXPOSED_ENTITIES[0], "mock")) - is False - ) + assert config.should_expose(State(CLOUD_NEVER_EXPOSED_ENTITIES[0], "mock")) is False async def test_missing_service_account(hass: HomeAssistant) -> None: diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index ff673ddfe24..6455128fce8 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -909,7 +909,7 @@ async def test_serialize_input_boolean(hass: HomeAssistant) -> None: """Test serializing an input boolean entity.""" state = State("input_boolean.bla", "on") entity = sh.GoogleEntity(hass, BASIC_CONFIG, state) - result = await entity.sync_serialize(None, "mock-uuid") + result = entity.sync_serialize(None, "mock-uuid") assert result == { "id": "input_boolean.bla", "attributes": {}, diff --git a/tests/components/homeassistant/test_exposed_entities.py b/tests/components/homeassistant/test_exposed_entities.py index 4f9a78625db..db82a696155 100644 --- a/tests/components/homeassistant/test_exposed_entities.py +++ b/tests/components/homeassistant/test_exposed_entities.py @@ -101,21 +101,21 @@ async def test_load_preferences(hass: HomeAssistant) -> None: exposed_entities.async_set_expose_new_entities("test1", True) exposed_entities.async_set_expose_new_entities("test2", False) - await exposed_entities.async_expose_entity("test1", "light.kitchen", True) - await exposed_entities.async_expose_entity("test1", "light.living_room", True) - await exposed_entities.async_expose_entity("test2", "light.kitchen", True) - await exposed_entities.async_expose_entity("test2", "light.kitchen", True) + exposed_entities.async_expose_entity("test1", "light.kitchen", True) + exposed_entities.async_expose_entity("test1", "light.living_room", True) + exposed_entities.async_expose_entity("test2", "light.kitchen", True) + exposed_entities.async_expose_entity("test2", "light.kitchen", True) assert list(exposed_entities._assistants) == ["test1", "test2"] - assert list(exposed_entities.data) == ["light.kitchen", "light.living_room"] + assert list(exposed_entities.entities) == ["light.kitchen", "light.living_room"] - await flush_store(exposed_entities.store) + await flush_store(exposed_entities._store) exposed_entities2 = ExposedEntities(hass) - await exposed_entities2.async_load() + await exposed_entities2.async_initialize() assert exposed_entities._assistants == exposed_entities2._assistants - assert exposed_entities.data == exposed_entities2.data + assert exposed_entities.entities == exposed_entities2.entities async def test_expose_entity( @@ -132,7 +132,7 @@ async def test_expose_entity( entry2 = entity_registry.async_get_or_create("test", "test", "unique2") exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - assert len(exposed_entities.data) == 0 + assert len(exposed_entities.entities) == 0 # Set options await ws_client.send_json_auto_id( @@ -151,7 +151,7 @@ async def test_expose_entity( assert entry1.options == {"cloud.alexa": {"should_expose": True}} entry2 = entity_registry.async_get(entry2.entity_id) assert entry2.options == {} - assert len(exposed_entities.data) == 0 + assert len(exposed_entities.entities) == 0 # Update options await ws_client.send_json_auto_id( @@ -176,7 +176,7 @@ async def test_expose_entity( "cloud.alexa": {"should_expose": False}, "cloud.google_assistant": {"should_expose": False}, } - assert len(exposed_entities.data) == 0 + assert len(exposed_entities.entities) == 0 async def test_expose_entity_unknown( @@ -189,7 +189,7 @@ async def test_expose_entity_unknown( await hass.async_block_till_done() exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] - assert len(exposed_entities.data) == 0 + assert len(exposed_entities.entities) == 0 # Set options await ws_client.send_json_auto_id( @@ -204,8 +204,8 @@ async def test_expose_entity_unknown( response = await ws_client.receive_json() assert response["success"] - assert len(exposed_entities.data) == 1 - assert exposed_entities.data == { + assert len(exposed_entities.entities) == 1 + assert exposed_entities.entities == { "test.test": ExposedEntity({"cloud.alexa": {"should_expose": True}}) } @@ -222,8 +222,8 @@ async def test_expose_entity_unknown( response = await ws_client.receive_json() assert response["success"] - assert len(exposed_entities.data) == 2 - assert exposed_entities.data == { + assert len(exposed_entities.entities) == 2 + assert exposed_entities.entities == { "test.test": ExposedEntity( { "cloud.alexa": {"should_expose": False}, @@ -292,7 +292,7 @@ async def test_expose_new_entities( assert response["result"] == {"expose_new": False} # Check if exposed - should be False - assert await async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False + assert async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False # Expose new entities to Alexa await ws_client.send_json_auto_id( @@ -315,12 +315,10 @@ async def test_expose_new_entities( assert response["result"] == {"expose_new": expose_new} # Check again if exposed - should still be False - assert await async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False + assert async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False # Check if exposed - should be True - assert ( - await async_should_expose(hass, "cloud.alexa", entry2.entity_id) == expose_new - ) + assert async_should_expose(hass, "cloud.alexa", entry2.entity_id) == expose_new async def test_listen_updates( @@ -342,21 +340,21 @@ async def test_listen_updates( entry = entity_registry.async_get_or_create("climate", "test", "unique1") # Call for another assistant - listener not called - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( "cloud.google_assistant", entry.entity_id, True ) assert len(calls) == 0 # Call for our assistant - listener called - await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) + exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) assert len(calls) == 1 # Settings not changed - listener not called - await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) + exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) assert len(calls) == 1 # Settings changed - listener called - await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, False) + exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, False) assert len(calls) == 2 @@ -375,10 +373,8 @@ async def test_get_assistant_settings( assert async_get_assistant_settings(hass, "cloud.alexa") == {} - await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) - await exposed_entities.async_expose_entity( - "cloud.alexa", "light.not_in_registry", True - ) + exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) + exposed_entities.async_expose_entity("cloud.alexa", "light.not_in_registry", True) assert async_get_assistant_settings(hass, "cloud.alexa") == snapshot assert async_get_assistant_settings(hass, "cloud.google_assistant") == snapshot @@ -412,45 +408,38 @@ async def test_should_expose( assert response["success"] # Unknown entity is not exposed - assert await async_should_expose(hass, "test.test", "test.test") is False + assert async_should_expose(hass, "test.test", "test.test") is False # Blocked entity is not exposed - assert await async_should_expose(hass, "cloud.alexa", entities["blocked"]) is False + assert async_should_expose(hass, "cloud.alexa", entities["blocked"]) is False # Lock is exposed - assert await async_should_expose(hass, "cloud.alexa", entities["lock"]) is True + assert async_should_expose(hass, "cloud.alexa", entities["lock"]) is True # Binary sensor without device class is not exposed - assert ( - await async_should_expose(hass, "cloud.alexa", entities["binary_sensor"]) - is False - ) + assert async_should_expose(hass, "cloud.alexa", entities["binary_sensor"]) is False # Binary sensor with certain device class is exposed - assert ( - await async_should_expose(hass, "cloud.alexa", entities["door_sensor"]) is True - ) + assert async_should_expose(hass, "cloud.alexa", entities["door_sensor"]) is True # Sensor without device class is not exposed - assert await async_should_expose(hass, "cloud.alexa", entities["sensor"]) is False + assert async_should_expose(hass, "cloud.alexa", entities["sensor"]) is False # Sensor with certain device class is exposed assert ( - await async_should_expose(hass, "cloud.alexa", entities["temperature_sensor"]) - is True + async_should_expose(hass, "cloud.alexa", entities["temperature_sensor"]) is True ) # The second time we check, it should load it from storage assert ( - await async_should_expose(hass, "cloud.alexa", entities["temperature_sensor"]) - is True + async_should_expose(hass, "cloud.alexa", entities["temperature_sensor"]) is True ) # Check with a different assistant exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] exposed_entities.async_set_expose_new_entities("cloud.no_default_expose", False) assert ( - await async_should_expose( + async_should_expose( hass, "cloud.no_default_expose", entities["temperature_sensor"] ) is False @@ -481,13 +470,13 @@ async def test_should_expose_hidden_categorized( entity_registry.async_get_or_create( "lock", "test", "unique2", hidden_by=er.RegistryEntryHider.USER ) - assert await async_should_expose(hass, "cloud.alexa", "lock.test_unique2") is False + assert async_should_expose(hass, "cloud.alexa", "lock.test_unique2") is False # Entity with category is not exposed entity_registry.async_get_or_create( "lock", "test", "unique3", entity_category=EntityCategory.CONFIG ) - assert await async_should_expose(hass, "cloud.alexa", "lock.test_unique3") is False + assert async_should_expose(hass, "cloud.alexa", "lock.test_unique3") is False async def test_list_exposed_entities( @@ -555,8 +544,8 @@ async def test_listeners( callbacks = [] exposed_entities.async_listen_entity_updates("test1", lambda: callbacks.append(1)) - await async_expose_entity(hass, "test1", "light.kitchen", True) + async_expose_entity(hass, "test1", "light.kitchen", True) assert len(callbacks) == 1 entry1 = entity_registry.async_get_or_create("switch", "test", "unique1") - await async_expose_entity(hass, "test1", entry1.entity_id, True) + async_expose_entity(hass, "test1", entry1.entity_id, True) diff --git a/tests/components/switch_as_x/test_init.py b/tests/components/switch_as_x/test_init.py index ad7bf732dcc..fac744d0c0e 100644 --- a/tests/components/switch_as_x/test_init.py +++ b/tests/components/switch_as_x/test_init.py @@ -702,7 +702,7 @@ async def test_import_expose_settings_1( original_name="ABC", ) for assistant, should_expose in EXPOSE_SETTINGS.items(): - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( hass, assistant, switch_entity_entry.entity_id, should_expose ) @@ -760,7 +760,7 @@ async def test_import_expose_settings_2( original_name="ABC", ) for assistant, should_expose in EXPOSE_SETTINGS.items(): - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( hass, assistant, switch_entity_entry.entity_id, should_expose ) @@ -785,7 +785,7 @@ async def test_import_expose_settings_2( suggested_object_id="abc", ) for assistant, should_expose in EXPOSE_SETTINGS.items(): - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( hass, assistant, switch_as_x_entity_entry.entity_id, not should_expose ) @@ -850,7 +850,7 @@ async def test_restore_expose_settings( suggested_object_id="abc", ) for assistant, should_expose in EXPOSE_SETTINGS.items(): - await exposed_entities.async_expose_entity( + exposed_entities.async_expose_entity( hass, assistant, switch_as_x_entity_entry.entity_id, should_expose )