diff --git a/homeassistant/components/cloud/alexa_config.py b/homeassistant/components/cloud/alexa_config.py index dc1f5c11b5d..a45469c8f97 100644 --- a/homeassistant/components/cloud/alexa_config.py +++ b/homeassistant/components/cloud/alexa_config.py @@ -271,15 +271,25 @@ class AlexaConfig(alexa_config.AbstractConfig): if not self.enabled or not self._cloud.is_logged_in: return - action = event.data["action"] entity_id = event.data["entity_id"] + + if not self.should_expose(entity_id): + return + + action = event.data["action"] to_update = [] to_remove = [] - if action == "create" and self.should_expose(entity_id): + if action == "create": to_update.append(entity_id) - elif action == "remove" and self.should_expose(entity_id): + elif action == "remove": to_remove.append(entity_id) + elif action == "update" and bool( + set(event.data["changes"]) & entity_registry.ENTITY_DESCRIBING_ATTRIBUTES + ): + to_update.append(entity_id) + if "old_entity_id" in event.data: + to_remove.append(event.data["old_entity_id"]) try: await self._sync_helper(to_update, to_remove) diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index 8420a1bea7e..bb6dcaa2fe2 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -33,12 +33,6 @@ class CloudGoogleConfig(AbstractConfig): self._cur_entity_prefs = self._prefs.google_entity_configs self._sync_entities_lock = asyncio.Lock() - prefs.async_listen_updates(self._async_prefs_updated) - hass.bus.async_listen( - entity_registry.EVENT_ENTITY_REGISTRY_UPDATED, - self._handle_entity_registry_updated, - ) - @property def enabled(self): """Return if Google is enabled.""" @@ -83,6 +77,13 @@ class CloudGoogleConfig(AbstractConfig): # Remove bad data that was there until 0.103.6 - Jan 6, 2020 self._store.pop_agent_user_id(self._user) + self._prefs.async_listen_updates(self._async_prefs_updated) + + self.hass.bus.async_listen( + entity_registry.EVENT_ENTITY_REGISTRY_UPDATED, + self._handle_entity_registry_updated, + ) + def should_expose(self, state): """If a state object should be exposed.""" return self._should_expose_entity_id(state.entity_id) @@ -160,8 +161,14 @@ class CloudGoogleConfig(AbstractConfig): if not self.enabled or not self._cloud.is_logged_in: return + # Only consider entity registry updates if info relevant for Google has changed + if event.data["action"] == "update" and not bool( + set(event.data["changes"]) & entity_registry.ENTITY_DESCRIBING_ATTRIBUTES + ): + return + entity_id = event.data["entity_id"] # Schedule a sync if a change was made to an entity that Google knows about if self._should_expose_entity_id(entity_id): - await self.async_sync_entities_all() + self.async_schedule_google_sync_all() diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index d73812c207b..10de8564fca 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -62,6 +62,18 @@ ATTR_RESTORED = "restored" STORAGE_VERSION = 1 STORAGE_KEY = "core.entity_registry" +# Attributes relevant to describing entity +# to external services. +ENTITY_DESCRIBING_ATTRIBUTES = { + "entity_id", + "name", + "original_name", + "capabilities", + "supported_features", + "device_class", + "unit_of_measurement", +} + @attr.s(slots=True, frozen=True) class RegistryEntry: diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index 508626b43f0..f65b810d690 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -165,10 +165,21 @@ async def test_alexa_entity_registry_sync(hass, mock_cloud_login, cloud_prefs): "action": "update", "entity_id": "light.kitchen", "changes": ["entity_id"], + "old_entity_id": "light.living_room", }, ) await hass.async_block_till_done() + assert to_update == ["light.kitchen"] + assert to_remove == ["light.living_room"] + + with patch_sync_helper() as (to_update, to_remove): + hass.bus.async_fire( + EVENT_ENTITY_REGISTRY_UPDATED, + {"action": "update", "entity_id": "light.kitchen", "changes": ["icon"]}, + ) + await hass.async_block_till_done() + assert to_update == [] assert to_remove == [] diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 1070730ba96..2474851cce8 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -1,5 +1,7 @@ """Test the Cloud Google Config.""" -from unittest.mock import Mock, patch +from unittest.mock import Mock + +from asynctest import patch from homeassistant.components.cloud import GACTIONS_SCHEMA from homeassistant.components.cloud.google_config import CloudGoogleConfig @@ -105,30 +107,27 @@ async def test_google_entity_registry_sync(hass, mock_cloud_login, cloud_prefs): await config.async_connect_agent_user("mock-user-id") with patch.object( - config, "async_sync_entities", side_effect=mock_coro + config, "async_schedule_google_sync_all", side_effect=mock_coro ) as mock_sync, patch.object(ga_helpers, "SYNC_DELAY", 0): + # Created entity hass.bus.async_fire( EVENT_ENTITY_REGISTRY_UPDATED, {"action": "create", "entity_id": "light.kitchen"}, ) await hass.async_block_till_done() - assert len(mock_sync.mock_calls) == 1 + assert len(mock_sync.mock_calls) == 1 - with patch.object( - config, "async_sync_entities", side_effect=mock_coro - ) as mock_sync, patch.object(ga_helpers, "SYNC_DELAY", 0): + # Removed entity hass.bus.async_fire( EVENT_ENTITY_REGISTRY_UPDATED, {"action": "remove", "entity_id": "light.kitchen"}, ) await hass.async_block_till_done() - assert len(mock_sync.mock_calls) == 1 + assert len(mock_sync.mock_calls) == 2 - with patch.object( - config, "async_sync_entities", side_effect=mock_coro - ) as mock_sync, patch.object(ga_helpers, "SYNC_DELAY", 0): + # Entity registry updated with relevant changes hass.bus.async_fire( EVENT_ENTITY_REGISTRY_UPDATED, { @@ -139,4 +138,13 @@ async def test_google_entity_registry_sync(hass, mock_cloud_login, cloud_prefs): ) await hass.async_block_till_done() - assert len(mock_sync.mock_calls) == 1 + assert len(mock_sync.mock_calls) == 3 + + # Entity registry updated with non-relevant changes + hass.bus.async_fire( + EVENT_ENTITY_REGISTRY_UPDATED, + {"action": "update", "entity_id": "light.kitchen", "changes": ["icon"]}, + ) + await hass.async_block_till_done() + + assert len(mock_sync.mock_calls) == 3