Allow setting google disable 2fa flag on any entity (#92403)

* Allow setting google disable 2fa flag on any entity

* Fix test

* Include disable_2fa flag in cloud/google_assistant/entities/get
This commit is contained in:
Erik Montnemery 2023-05-03 15:55:38 +02:00 committed by GitHub
parent 20942ab26f
commit 31de1b17e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 113 additions and 82 deletions

View File

@ -11,6 +11,7 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.google_assistant import DOMAIN as GOOGLE_DOMAIN from homeassistant.components.google_assistant import DOMAIN as GOOGLE_DOMAIN
from homeassistant.components.google_assistant.helpers import AbstractConfig from homeassistant.components.google_assistant.helpers import AbstractConfig
from homeassistant.components.homeassistant.exposed_entities import ( from homeassistant.components.homeassistant.exposed_entities import (
async_get_entity_settings,
async_listen_entity_updates, async_listen_entity_updates,
async_should_expose, async_should_expose,
) )
@ -23,6 +24,7 @@ from homeassistant.core import (
callback, callback,
split_entity_id, split_entity_id,
) )
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er, start from homeassistant.helpers import device_registry as dr, entity_registry as er, start
from homeassistant.helpers.entity import get_device_class from homeassistant.helpers.entity import get_device_class
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -289,14 +291,13 @@ class CloudGoogleConfig(AbstractConfig):
def should_2fa(self, state): def should_2fa(self, state):
"""If an entity should be checked for 2FA.""" """If an entity should be checked for 2FA."""
entity_registry = er.async_get(self.hass) try:
settings = async_get_entity_settings(self.hass, state.entity_id)
registry_entry = entity_registry.async_get(state.entity_id) except HomeAssistantError:
if not registry_entry:
# Handle the entity has been removed # Handle the entity has been removed
return False return False
assistant_options = registry_entry.options.get(CLOUD_GOOGLE, {}) assistant_options = settings.get(CLOUD_GOOGLE, {})
return not assistant_options.get(PREF_DISABLE_2FA, DEFAULT_DISABLE_2FA) return not assistant_options.get(PREF_DISABLE_2FA, DEFAULT_DISABLE_2FA)
async def async_report_state(self, message, agent_user_id: str): async def async_report_state(self, message, agent_user_id: str):

View File

@ -1,6 +1,7 @@
"""The HTTP api to control the cloud integration.""" """The HTTP api to control the cloud integration."""
import asyncio import asyncio
from collections.abc import Mapping from collections.abc import Mapping
from contextlib import suppress
import dataclasses import dataclasses
from functools import wraps from functools import wraps
from http import HTTPStatus from http import HTTPStatus
@ -21,10 +22,12 @@ from homeassistant.components.alexa import (
errors as alexa_errors, errors as alexa_errors,
) )
from homeassistant.components.google_assistant import helpers as google_helpers from homeassistant.components.google_assistant import helpers as google_helpers
from homeassistant.components.homeassistant import exposed_entities
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util.location import async_detect_location_info from homeassistant.util.location import async_detect_location_info
@ -587,10 +590,16 @@ async def google_assistant_get(
) )
return return
assistant_options: Mapping[str, Any] = {}
with suppress(HomeAssistantError, KeyError):
settings = exposed_entities.async_get_entity_settings(hass, entity_id)
assistant_options = settings[CLOUD_GOOGLE]
result = { result = {
"entity_id": entity.entity_id, "entity_id": entity.entity_id,
"traits": [trait.name for trait in entity.traits()], "traits": [trait.name for trait in entity.traits()],
"might_2fa": entity.might_2fa_traits(), "might_2fa": entity.might_2fa_traits(),
PREF_DISABLE_2FA: assistant_options.get(PREF_DISABLE_2FA),
} }
connection.send_result(msg["id"], result) connection.send_result(msg["id"], result)
@ -645,27 +654,19 @@ async def google_assistant_update(
msg: dict[str, Any], msg: dict[str, Any],
) -> None: ) -> None:
"""Update google assistant entity config.""" """Update google assistant entity config."""
entity_registry = er.async_get(hass)
entity_id: str = msg["entity_id"] entity_id: str = msg["entity_id"]
if not (registry_entry := entity_registry.async_get(entity_id)): assistant_options: Mapping[str, Any] = {}
connection.send_error( with suppress(HomeAssistantError, KeyError):
msg["id"], settings = exposed_entities.async_get_entity_settings(hass, entity_id)
websocket_api.const.ERR_NOT_ALLOWED, assistant_options = settings[CLOUD_GOOGLE]
f"can't configure {entity_id}",
)
return
disable_2fa = msg[PREF_DISABLE_2FA] disable_2fa = msg[PREF_DISABLE_2FA]
assistant_options: Mapping[str, Any] if assistant_options.get(PREF_DISABLE_2FA) == disable_2fa:
if (
assistant_options := registry_entry.options.get(CLOUD_GOOGLE, {})
) and assistant_options.get(PREF_DISABLE_2FA) == disable_2fa:
return return
assistant_options = assistant_options | {PREF_DISABLE_2FA: disable_2fa} exposed_entities.async_set_assistant_option(
entity_registry.async_update_entity_options( hass, CLOUD_GOOGLE, entity_id, PREF_DISABLE_2FA, disable_2fa
entity_id, CLOUD_GOOGLE, assistant_options
) )
connection.send_result(msg["id"]) connection.send_result(msg["id"])

View File

@ -132,50 +132,52 @@ class ExposedEntities:
self._listeners.setdefault(assistant, []).append(listener) self._listeners.setdefault(assistant, []).append(listener)
@callback @callback
def async_expose_entity( def async_set_assistant_option(
self, assistant: str, entity_id: str, should_expose: bool self, assistant: str, entity_id: str, key: str, value: Any
) -> None: ) -> None:
"""Expose an entity to an assistant. """Set an option for an assistant.
Notify listeners if expose flag was changed. 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)): if not (registry_entry := entity_registry.async_get(entity_id)):
return self._async_expose_legacy_entity(assistant, entity_id, should_expose) return self._async_set_legacy_assistant_option(
assistant, entity_id, key, value
)
assistant_options: Mapping[str, Any] assistant_options: Mapping[str, Any]
if ( if (
assistant_options := registry_entry.options.get(assistant, {}) assistant_options := registry_entry.options.get(assistant, {})
) and assistant_options.get("should_expose") == should_expose: ) and assistant_options.get(key) == value:
return return
assistant_options = assistant_options | {"should_expose": should_expose} assistant_options = assistant_options | {key: value}
entity_registry.async_update_entity_options( entity_registry.async_update_entity_options(
entity_id, assistant, assistant_options entity_id, assistant, assistant_options
) )
for listener in self._listeners.get(assistant, []): for listener in self._listeners.get(assistant, []):
listener() listener()
def _async_expose_legacy_entity( def _async_set_legacy_assistant_option(
self, assistant: str, entity_id: str, should_expose: bool self, assistant: str, entity_id: str, key: str, value: Any
) -> None: ) -> None:
"""Expose an entity to an assistant. """Set an option for an assistant.
Notify listeners if expose flag was changed. Notify listeners if expose flag was changed.
""" """
if ( if (
(exposed_entity := self.entities.get(entity_id)) (exposed_entity := self.entities.get(entity_id))
and (assistant_options := exposed_entity.assistants.get(assistant, {})) and (assistant_options := exposed_entity.assistants.get(assistant, {}))
and assistant_options.get("should_expose") == should_expose and assistant_options.get(key) == value
): ):
return return
if exposed_entity: if exposed_entity:
new_exposed_entity = self._update_exposed_entity( new_exposed_entity = self._update_exposed_entity(
assistant, entity_id, should_expose assistant, entity_id, key, value
) )
else: else:
new_exposed_entity = self._new_exposed_entity(assistant, should_expose) new_exposed_entity = self._new_exposed_entity(assistant, key, value)
self.entities[entity_id] = new_exposed_entity self.entities[entity_id] = new_exposed_entity
self._async_schedule_save() self._async_schedule_save()
for listener in self._listeners.get(assistant, []): for listener in self._listeners.get(assistant, []):
@ -282,10 +284,12 @@ class ExposedEntities:
if exposed_entity: if exposed_entity:
new_exposed_entity = self._update_exposed_entity( new_exposed_entity = self._update_exposed_entity(
assistant, entity_id, should_expose assistant, entity_id, "should_expose", should_expose
) )
else: else:
new_exposed_entity = self._new_exposed_entity(assistant, should_expose) new_exposed_entity = self._new_exposed_entity(
assistant, "should_expose", should_expose
)
self.entities[entity_id] = new_exposed_entity self.entities[entity_id] = new_exposed_entity
self._async_schedule_save() self._async_schedule_save()
@ -322,22 +326,21 @@ class ExposedEntities:
return False return False
def _update_exposed_entity( def _update_exposed_entity(
self, self, assistant: str, entity_id: str, key: str, value: Any
assistant: str,
entity_id: str,
should_expose: bool,
) -> ExposedEntity: ) -> ExposedEntity:
"""Update an exposed entity.""" """Update an exposed entity."""
entity = self.entities[entity_id] entity = self.entities[entity_id]
assistants = dict(entity.assistants) assistants = dict(entity.assistants)
old_settings = assistants.get(assistant, {}) old_settings = assistants.get(assistant, {})
assistants[assistant] = old_settings | {"should_expose": should_expose} assistants[assistant] = old_settings | {key: value}
return ExposedEntity(assistants) return ExposedEntity(assistants)
def _new_exposed_entity(self, assistant: str, should_expose: bool) -> ExposedEntity: def _new_exposed_entity(
self, assistant: str, key: str, value: Any
) -> ExposedEntity:
"""Create a new exposed entity.""" """Create a new exposed entity."""
return ExposedEntity( return ExposedEntity(
assistants={assistant: {"should_expose": should_expose}}, assistants={assistant: {key: value}},
) )
async def _async_load_data(self) -> SerializedExposedEntities | None: async def _async_load_data(self) -> SerializedExposedEntities | None:
@ -409,12 +412,9 @@ def ws_expose_entity(
) )
return return
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
for entity_id in entity_ids: for entity_id in entity_ids:
for assistant in msg["assistants"]: for assistant in msg["assistants"]:
exposed_entities.async_expose_entity( async_expose_entity(hass, assistant, entity_id, msg["should_expose"])
assistant, entity_id, msg["should_expose"]
)
connection.send_result(msg["id"]) connection.send_result(msg["id"])
@ -513,8 +513,9 @@ def async_expose_entity(
should_expose: bool, should_expose: bool,
) -> None: ) -> None:
"""Get assistant expose settings for an entity.""" """Get assistant expose settings for an entity."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] async_set_assistant_option(
exposed_entities.async_expose_entity(assistant, entity_id, should_expose) hass, assistant, entity_id, "should_expose", should_expose
)
@callback @callback
@ -522,3 +523,15 @@ def async_should_expose(hass: HomeAssistant, assistant: str, entity_id: str) ->
"""Return True if an entity should be exposed to an assistant.""" """Return True if an entity should be exposed to an assistant."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
return exposed_entities.async_should_expose(assistant, entity_id) return exposed_entities.async_should_expose(assistant, entity_id)
@callback
def async_set_assistant_option(
hass: HomeAssistant, assistant: str, entity_id: str, option: str, value: Any
) -> None:
"""Set an option for an assistant.
Notify listeners if expose flag was changed.
"""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
exposed_entities.async_set_assistant_option(assistant, entity_id, option, value)

View File

@ -15,6 +15,7 @@ from homeassistant.components.cloud.prefs import CloudPreferences
from homeassistant.components.homeassistant.exposed_entities import ( from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES, DATA_EXPOSED_ENTITIES,
ExposedEntities, ExposedEntities,
async_expose_entity,
) )
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -40,8 +41,7 @@ def expose_new(hass, expose_new):
def expose_entity(hass, entity_id, should_expose): def expose_entity(hass, entity_id, should_expose):
"""Expose an entity to Alexa.""" """Expose an entity to Alexa."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] async_expose_entity(hass, "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( async def test_alexa_config_expose_entity_prefs(

View File

@ -16,6 +16,7 @@ from homeassistant.components.cloud.const import (
from homeassistant.components.homeassistant.exposed_entities import ( from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES, DATA_EXPOSED_ENTITIES,
ExposedEntities, ExposedEntities,
async_expose_entity,
) )
from homeassistant.const import CONTENT_TYPE_JSON from homeassistant.const import CONTENT_TYPE_JSON
from homeassistant.core import HomeAssistant, State from homeassistant.core import HomeAssistant, State
@ -267,9 +268,7 @@ async def test_google_config_expose_entity(
assert gconf.should_expose(state) assert gconf.should_expose(state)
exposed_entities.async_expose_entity( async_expose_entity(hass, "cloud.google_assistant", entity_entry.entity_id, False)
"cloud.google_assistant", entity_entry.entity_id, False
)
assert not gconf.should_expose(state) assert not gconf.should_expose(state)

View File

@ -18,6 +18,7 @@ from homeassistant.components.google_assistant import helpers as ga_helpers
from homeassistant.components.homeassistant.exposed_entities import ( from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES, DATA_EXPOSED_ENTITIES,
ExposedEntities, ExposedEntities,
async_expose_entity,
) )
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EntityCategory from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EntityCategory
from homeassistant.core import CoreState, HomeAssistant, State from homeassistant.core import CoreState, HomeAssistant, State
@ -48,10 +49,7 @@ def expose_new(hass, expose_new):
def expose_entity(hass, entity_id, should_expose): def expose_entity(hass, entity_id, should_expose):
"""Expose an entity to Google.""" """Expose an entity to Google."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] async_expose_entity(hass, "cloud.google_assistant", entity_id, should_expose)
exposed_entities.async_expose_entity(
"cloud.google_assistant", entity_id, should_expose
)
async def test_google_update_report_state( async def test_google_update_report_state(

View File

@ -14,6 +14,7 @@ from homeassistant.components.alexa import errors as alexa_errors
from homeassistant.components.alexa.entities import LightCapabilities from homeassistant.components.alexa.entities import LightCapabilities
from homeassistant.components.cloud.const import DOMAIN from homeassistant.components.cloud.const import DOMAIN
from homeassistant.components.google_assistant.helpers import GoogleEntity from homeassistant.components.google_assistant.helpers import GoogleEntity
from homeassistant.components.homeassistant import exposed_entities
from homeassistant.core import HomeAssistant, State from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -842,6 +843,7 @@ async def test_get_google_entity(
response = await client.receive_json() response = await client.receive_json()
assert response["success"] assert response["success"]
assert response["result"] == { assert response["result"] == {
"disable_2fa": None,
"entity_id": "light.kitchen", "entity_id": "light.kitchen",
"might_2fa": False, "might_2fa": False,
"traits": ["action.devices.traits.OnOff"], "traits": ["action.devices.traits.OnOff"],
@ -853,6 +855,30 @@ async def test_get_google_entity(
response = await client.receive_json() response = await client.receive_json()
assert response["success"] assert response["success"]
assert response["result"] == { assert response["result"] == {
"disable_2fa": None,
"entity_id": "cover.garage",
"might_2fa": True,
"traits": ["action.devices.traits.OpenClose"],
}
# Set the disable 2fa flag
await client.send_json_auto_id(
{
"type": "cloud/google_assistant/entities/update",
"entity_id": "cover.garage",
"disable_2fa": True,
}
)
response = await client.receive_json()
assert response["success"]
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "cover.garage"}
)
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"disable_2fa": True,
"entity_id": "cover.garage", "entity_id": "cover.garage",
"might_2fa": True, "might_2fa": True,
"traits": ["action.devices.traits.OpenClose"], "traits": ["action.devices.traits.OpenClose"],
@ -867,9 +893,6 @@ async def test_update_google_entity(
mock_cloud_login, mock_cloud_login,
) -> None: ) -> None:
"""Test that we can update config of a Google entity.""" """Test that we can update config of a Google entity."""
entry = entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
await client.send_json_auto_id( await client.send_json_auto_id(
{ {
@ -885,16 +908,16 @@ async def test_update_google_entity(
{ {
"type": "homeassistant/expose_entity", "type": "homeassistant/expose_entity",
"assistants": ["cloud.google_assistant"], "assistants": ["cloud.google_assistant"],
"entity_ids": [entry.entity_id], "entity_ids": ["light.kitchen"],
"should_expose": False, "should_expose": False,
} }
) )
response = await client.receive_json() response = await client.receive_json()
assert response["success"] assert response["success"]
assert entity_registry.async_get(entry.entity_id).options[ assert exposed_entities.async_get_entity_settings(hass, "light.kitchen") == {
"cloud.google_assistant" "cloud.google_assistant": {"disable_2fa": False, "should_expose": False}
] == {"disable_2fa": False, "should_expose": False} }
async def test_list_alexa_entities( async def test_list_alexa_entities(

View File

@ -7,6 +7,7 @@ from homeassistant.components import conversation
from homeassistant.components.homeassistant.exposed_entities import ( from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES, DATA_EXPOSED_ENTITIES,
ExposedEntities, ExposedEntities,
async_expose_entity,
) )
from homeassistant.helpers import intent from homeassistant.helpers import intent
@ -53,5 +54,4 @@ def expose_new(hass, expose_new):
def expose_entity(hass, entity_id, should_expose): def expose_entity(hass, entity_id, should_expose):
"""Expose an entity to the default agent.""" """Expose an entity to the default agent."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] async_expose_entity(hass, conversation.DOMAIN, entity_id, should_expose)
exposed_entities.async_expose_entity(conversation.DOMAIN, entity_id, should_expose)

View File

@ -451,8 +451,8 @@ async def test_execute(
hass: HomeAssistant, report_state, on, brightness, value hass: HomeAssistant, report_state, on, brightness, value
) -> None: ) -> None:
"""Test an execute command.""" """Test an execute command."""
await async_setup_component(hass, "light", {"light": {"platform": "demo"}})
await async_setup_component(hass, "homeassistant", {}) await async_setup_component(hass, "homeassistant", {})
await async_setup_component(hass, "light", {"light": {"platform": "demo"}})
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(

View File

@ -8,6 +8,7 @@ from homeassistant.components.homeassistant.exposed_entities import (
ExposedEntity, ExposedEntity,
async_expose_entity, async_expose_entity,
async_get_assistant_settings, async_get_assistant_settings,
async_get_entity_settings,
async_listen_entity_updates, async_listen_entity_updates,
async_should_expose, async_should_expose,
) )
@ -101,10 +102,10 @@ async def test_load_preferences(hass: HomeAssistant) -> None:
exposed_entities.async_set_expose_new_entities("test1", True) exposed_entities.async_set_expose_new_entities("test1", True)
exposed_entities.async_set_expose_new_entities("test2", False) exposed_entities.async_set_expose_new_entities("test2", False)
exposed_entities.async_expose_entity("test1", "light.kitchen", True) async_expose_entity(hass, "test1", "light.kitchen", True)
exposed_entities.async_expose_entity("test1", "light.living_room", True) async_expose_entity(hass, "test1", "light.living_room", True)
exposed_entities.async_expose_entity("test2", "light.kitchen", True) async_expose_entity(hass, "test2", "light.kitchen", True)
exposed_entities.async_expose_entity("test2", "light.kitchen", True) async_expose_entity(hass, "test2", "light.kitchen", True)
assert list(exposed_entities._assistants) == ["test1", "test2"] assert list(exposed_entities._assistants) == ["test1", "test2"]
assert list(exposed_entities.entities) == ["light.kitchen", "light.living_room"] assert list(exposed_entities.entities) == ["light.kitchen", "light.living_room"]
@ -334,27 +335,24 @@ async def test_listen_updates(
assert await async_setup_component(hass, "homeassistant", {}) assert await async_setup_component(hass, "homeassistant", {})
await hass.async_block_till_done() await hass.async_block_till_done()
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
async_listen_entity_updates(hass, "cloud.alexa", listener) async_listen_entity_updates(hass, "cloud.alexa", listener)
entry = entity_registry.async_get_or_create("climate", "test", "unique1") entry = entity_registry.async_get_or_create("climate", "test", "unique1")
# Call for another assistant - listener not called # Call for another assistant - listener not called
exposed_entities.async_expose_entity( async_expose_entity(hass, "cloud.google_assistant", entry.entity_id, True)
"cloud.google_assistant", entry.entity_id, True
)
assert len(calls) == 0 assert len(calls) == 0
# Call for our assistant - listener called # Call for our assistant - listener called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) async_expose_entity(hass, "cloud.alexa", entry.entity_id, True)
assert len(calls) == 1 assert len(calls) == 1
# Settings not changed - listener not called # Settings not changed - listener not called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) async_expose_entity(hass, "cloud.alexa", entry.entity_id, True)
assert len(calls) == 1 assert len(calls) == 1
# Settings changed - listener called # Settings changed - listener called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, False) async_expose_entity(hass, "cloud.alexa", entry.entity_id, False)
assert len(calls) == 2 assert len(calls) == 2
@ -367,19 +365,17 @@ async def test_get_assistant_settings(
assert await async_setup_component(hass, "homeassistant", {}) assert await async_setup_component(hass, "homeassistant", {})
await hass.async_block_till_done() await hass.async_block_till_done()
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
entry = entity_registry.async_get_or_create("climate", "test", "unique1") entry = entity_registry.async_get_or_create("climate", "test", "unique1")
assert async_get_assistant_settings(hass, "cloud.alexa") == {} assert async_get_assistant_settings(hass, "cloud.alexa") == {}
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True) async_expose_entity(hass, "cloud.alexa", entry.entity_id, True)
exposed_entities.async_expose_entity("cloud.alexa", "light.not_in_registry", True) async_expose_entity(hass, "cloud.alexa", "light.not_in_registry", True)
assert async_get_assistant_settings(hass, "cloud.alexa") == snapshot assert async_get_assistant_settings(hass, "cloud.alexa") == snapshot
assert async_get_assistant_settings(hass, "cloud.google_assistant") == snapshot assert async_get_assistant_settings(hass, "cloud.google_assistant") == snapshot
with pytest.raises(HomeAssistantError): with pytest.raises(HomeAssistantError):
exposed_entities.async_get_entity_settings("light.unknown") async_get_entity_settings(hass, "light.unknown")
@pytest.mark.parametrize( @pytest.mark.parametrize(