Google assistant: disconnect user agent when not found in google (#48233)

This commit is contained in:
Bram Kragten 2021-03-23 23:04:32 +01:00 committed by GitHub
parent 9f8b697e64
commit b1d0b37d2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 34 deletions

View File

@ -6,11 +6,7 @@ from hass_nabucasa import Cloud, cloud_api
from hass_nabucasa.google_report_state import ErrorResponse from hass_nabucasa.google_report_state import ErrorResponse
from homeassistant.components.google_assistant.helpers import AbstractConfig from homeassistant.components.google_assistant.helpers import AbstractConfig
from homeassistant.const import ( from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, HTTP_OK
CLOUD_NEVER_EXPOSED_ENTITIES,
EVENT_HOMEASSISTANT_STARTED,
HTTP_OK,
)
from homeassistant.core import CoreState, split_entity_id from homeassistant.core import CoreState, split_entity_id
from homeassistant.helpers import entity_registry from homeassistant.helpers import entity_registry
@ -87,8 +83,15 @@ class CloudGoogleConfig(AbstractConfig):
async def async_initialize(self): async def async_initialize(self):
"""Perform async initialization of config.""" """Perform async initialization of config."""
await super().async_initialize() await super().async_initialize()
# Remove bad data that was there until 0.103.6 - Jan 6, 2020
self._store.pop_agent_user_id(self._user) # Remove old/wrong user agent ids
remove_agent_user_ids = []
for agent_user_id in self._store.agent_user_ids:
if agent_user_id != self.agent_user_id:
remove_agent_user_ids.append(agent_user_id)
for agent_user_id in remove_agent_user_ids:
await self.async_disconnect_agent_user(agent_user_id)
self._prefs.async_listen_updates(self._async_prefs_updated) self._prefs.async_listen_updates(self._async_prefs_updated)
@ -198,17 +201,7 @@ class CloudGoogleConfig(AbstractConfig):
if not self._should_expose_entity_id(entity_id): if not self._should_expose_entity_id(entity_id):
return return
if self.hass.state == CoreState.running: if self.hass.state != CoreState.running:
self.async_schedule_google_sync_all()
return return
if self._sync_on_started: self.async_schedule_google_sync_all()
return
self._sync_on_started = True
async def sync_google(_):
"""Sync entities to Google."""
await self.async_sync_entities_all()
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, sync_google)

View File

@ -15,9 +15,10 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
CLOUD_NEVER_EXPOSED_ENTITIES, CLOUD_NEVER_EXPOSED_ENTITIES,
CONF_NAME, CONF_NAME,
EVENT_HOMEASSISTANT_STARTED,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
from homeassistant.core import Context, HomeAssistant, State, callback from homeassistant.core import Context, CoreState, HomeAssistant, State, callback
from homeassistant.helpers.area_registry import AreaEntry from homeassistant.helpers.area_registry import AreaEntry
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.entity_registry import RegistryEntry from homeassistant.helpers.entity_registry import RegistryEntry
@ -104,6 +105,16 @@ class AbstractConfig(ABC):
self._store = GoogleConfigStore(self.hass) self._store = GoogleConfigStore(self.hass)
await self._store.async_load() await self._store.async_load()
if self.hass.state == CoreState.running:
await self.async_sync_entities_all()
return
async def sync_google(_):
"""Sync entities to Google."""
await self.async_sync_entities_all()
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, sync_google)
@property @property
def enabled(self): def enabled(self):
"""Return if Google is enabled.""" """Return if Google is enabled."""
@ -193,7 +204,10 @@ class AbstractConfig(ABC):
"""Sync all entities to Google.""" """Sync all entities to Google."""
# Remove any pending sync # Remove any pending sync
self._google_sync_unsub.pop(agent_user_id, lambda: None)() self._google_sync_unsub.pop(agent_user_id, lambda: None)()
return await self._async_request_sync_devices(agent_user_id) status = await self._async_request_sync_devices(agent_user_id)
if status == 404:
await self.async_disconnect_agent_user(agent_user_id)
return status
async def async_sync_entities_all(self): async def async_sync_entities_all(self):
"""Sync all entities to Google for all registered agents.""" """Sync all entities to Google for all registered agents."""

View File

@ -135,11 +135,12 @@ class GoogleConfig(AbstractConfig):
async def _async_request_sync_devices(self, agent_user_id: str): async def _async_request_sync_devices(self, agent_user_id: str):
if CONF_SERVICE_ACCOUNT in self._config: if CONF_SERVICE_ACCOUNT in self._config:
await self.async_call_homegraph_api( return await self.async_call_homegraph_api(
REQUEST_SYNC_BASE_URL, {"agentUserId": agent_user_id} REQUEST_SYNC_BASE_URL, {"agentUserId": agent_user_id}
) )
else:
_LOGGER.error("No configuration for request_sync available") _LOGGER.error("No configuration for request_sync available")
return HTTP_INTERNAL_SERVER_ERROR
async def _async_update_token(self, force=False): async def _async_update_token(self, force=False):
if CONF_SERVICE_ACCOUNT not in self._config: if CONF_SERVICE_ACCOUNT not in self._config:

View File

@ -1,5 +1,5 @@
"""Test the Cloud Google Config.""" """Test the Cloud Google Config."""
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import Mock, patch
import pytest import pytest
@ -41,21 +41,19 @@ async def test_google_update_report_state(mock_conf, hass, cloud_prefs):
assert len(mock_report_state.mock_calls) == 1 assert len(mock_report_state.mock_calls) == 1
async def test_sync_entities(aioclient_mock, hass, cloud_prefs): async def test_sync_entities(mock_conf, hass, cloud_prefs):
"""Test sync devices.""" """Test sync devices."""
config = CloudGoogleConfig( await mock_conf.async_initialize()
hass, await mock_conf.async_connect_agent_user("mock-user-id")
GACTIONS_SCHEMA({}),
"mock-user-id", assert len(mock_conf._store.agent_user_ids) == 1
cloud_prefs,
Mock(auth=Mock(async_check_token=AsyncMock())),
)
with patch( with patch(
"hass_nabucasa.cloud_api.async_google_actions_request_sync", "hass_nabucasa.cloud_api.async_google_actions_request_sync",
return_value=Mock(status=HTTP_NOT_FOUND), return_value=Mock(status=HTTP_NOT_FOUND),
) as mock_request_sync: ) as mock_request_sync:
assert await config.async_sync_entities("user") == HTTP_NOT_FOUND assert await mock_conf.async_sync_entities("mock-user-id") == HTTP_NOT_FOUND
assert len(mock_conf._store.agent_user_ids) == 0
assert len(mock_request_sync.mock_calls) == 1 assert len(mock_request_sync.mock_calls) == 1
@ -165,7 +163,29 @@ async def test_google_entity_registry_sync(hass, mock_cloud_login, cloud_prefs):
assert len(mock_sync.mock_calls) == 3 assert len(mock_sync.mock_calls) == 3
async def test_sync_google_when_started(hass, mock_cloud_login, cloud_prefs):
"""Test Google config syncs on init."""
config = CloudGoogleConfig(
hass, GACTIONS_SCHEMA({}), "mock-user-id", cloud_prefs, hass.data["cloud"]
)
with patch.object(config, "async_sync_entities_all") as mock_sync: with patch.object(config, "async_sync_entities_all") as mock_sync:
await config.async_initialize()
await config.async_connect_agent_user("mock-user-id")
assert len(mock_sync.mock_calls) == 1
async def test_sync_google_on_home_assistant_start(hass, mock_cloud_login, cloud_prefs):
"""Test Google config syncs when home assistant started."""
config = CloudGoogleConfig(
hass, GACTIONS_SCHEMA({}), "mock-user-id", cloud_prefs, hass.data["cloud"]
)
hass.state = CoreState.starting
with patch.object(config, "async_sync_entities_all") as mock_sync:
await config.async_initialize()
await config.async_connect_agent_user("mock-user-id")
assert len(mock_sync.mock_calls) == 0
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(mock_sync.mock_calls) == 1 assert len(mock_sync.mock_calls) == 1