Cloud: allow managing Alexa entities via UI (#24522)
* Clean up Alexa config * Cloud: Manage Alexa entities via UI * Add tests for new cloud APIspull/24544/head
parent
08591dae0e
commit
6c5124e12a
|
@ -1,13 +1,33 @@
|
|||
"""Config helpers for Alexa."""
|
||||
|
||||
|
||||
class Config:
|
||||
class AbstractConfig:
|
||||
"""Hold the configuration for Alexa."""
|
||||
|
||||
def __init__(self, endpoint, async_get_access_token, should_expose,
|
||||
entity_config=None):
|
||||
"""Initialize the configuration."""
|
||||
self.endpoint = endpoint
|
||||
self.async_get_access_token = async_get_access_token
|
||||
self.should_expose = should_expose
|
||||
self.entity_config = entity_config or {}
|
||||
@property
|
||||
def supports_auth(self):
|
||||
"""Return if config supports auth."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
"""Endpoint for report state."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def entity_config(self):
|
||||
"""Return entity config."""
|
||||
return {}
|
||||
|
||||
def should_expose(self, entity_id):
|
||||
"""If an entity should be exposed."""
|
||||
# pylint: disable=no-self-use
|
||||
return False
|
||||
|
||||
async def async_get_access_token(self):
|
||||
"""Get an access token."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def async_accept_grant(self, code):
|
||||
"""Accept a grant."""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -48,8 +48,6 @@ API_CHANGE = 'change'
|
|||
CONF_DESCRIPTION = 'description'
|
||||
CONF_DISPLAY_CATEGORIES = 'display_categories'
|
||||
|
||||
AUTH_KEY = "alexa.smart_home.auth"
|
||||
|
||||
API_TEMP_UNITS = {
|
||||
TEMP_FAHRENHEIT: 'FAHRENHEIT',
|
||||
TEMP_CELSIUS: 'CELSIUS',
|
||||
|
|
|
@ -31,7 +31,6 @@ from homeassistant.components import cover, fan, group, light, media_player
|
|||
from homeassistant.util.temperature import convert as convert_temperature
|
||||
|
||||
from .const import (
|
||||
AUTH_KEY,
|
||||
API_TEMP_UNITS,
|
||||
API_THERMOSTAT_MODES,
|
||||
Cause,
|
||||
|
@ -86,8 +85,8 @@ async def async_api_accept_grant(hass, config, directive, context):
|
|||
auth_code = directive.payload['grant']['code']
|
||||
_LOGGER.debug("AcceptGrant code: %s", auth_code)
|
||||
|
||||
if AUTH_KEY in hass.data:
|
||||
await hass.data[AUTH_KEY].async_do_auth(auth_code)
|
||||
if config.supports_auth:
|
||||
await config.async_accept_grant(auth_code)
|
||||
await async_enable_proactive_mode(hass, config)
|
||||
|
||||
return directive.response(
|
||||
|
|
|
@ -5,9 +5,8 @@ from homeassistant import core
|
|||
from homeassistant.components.http.view import HomeAssistantView
|
||||
|
||||
from .auth import Auth
|
||||
from .config import Config
|
||||
from .config import AbstractConfig
|
||||
from .const import (
|
||||
AUTH_KEY,
|
||||
CONF_CLIENT_ID,
|
||||
CONF_CLIENT_SECRET,
|
||||
CONF_ENDPOINT,
|
||||
|
@ -21,6 +20,47 @@ _LOGGER = logging.getLogger(__name__)
|
|||
SMART_HOME_HTTP_ENDPOINT = '/api/alexa/smart_home'
|
||||
|
||||
|
||||
class AlexaConfig(AbstractConfig):
|
||||
"""Alexa config."""
|
||||
|
||||
def __init__(self, hass, config):
|
||||
"""Initialize Alexa config."""
|
||||
self._config = config
|
||||
|
||||
if config.get(CONF_CLIENT_ID) and config.get(CONF_CLIENT_SECRET):
|
||||
self._auth = Auth(hass, config[CONF_CLIENT_ID],
|
||||
config[CONF_CLIENT_SECRET])
|
||||
else:
|
||||
self._auth = None
|
||||
|
||||
@property
|
||||
def supports_auth(self):
|
||||
"""Return if config supports auth."""
|
||||
return self._auth is not None
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
"""Endpoint for report state."""
|
||||
return self._config.get(CONF_ENDPOINT)
|
||||
|
||||
@property
|
||||
def entity_config(self):
|
||||
"""Return entity config."""
|
||||
return self._config.get(CONF_ENTITY_CONFIG, {})
|
||||
|
||||
def should_expose(self, entity_id):
|
||||
"""If an entity should be exposed."""
|
||||
return self._config[CONF_FILTER](entity_id)
|
||||
|
||||
async def async_get_access_token(self):
|
||||
"""Get an access token."""
|
||||
return await self._auth.async_get_access_token()
|
||||
|
||||
async def async_accept_grant(self, code):
|
||||
"""Accept a grant."""
|
||||
return await self._auth.async_do_auth(code)
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Activate Smart Home functionality of Alexa component.
|
||||
|
||||
|
@ -30,23 +70,10 @@ async def async_setup(hass, config):
|
|||
Even if that's disabled, the functionality in this module may still be used
|
||||
by the cloud component which will call async_handle_message directly.
|
||||
"""
|
||||
if config.get(CONF_CLIENT_ID) and config.get(CONF_CLIENT_SECRET):
|
||||
hass.data[AUTH_KEY] = Auth(hass, config[CONF_CLIENT_ID],
|
||||
config[CONF_CLIENT_SECRET])
|
||||
|
||||
async_get_access_token = \
|
||||
hass.data[AUTH_KEY].async_get_access_token if AUTH_KEY in hass.data \
|
||||
else None
|
||||
|
||||
smart_home_config = Config(
|
||||
endpoint=config.get(CONF_ENDPOINT),
|
||||
async_get_access_token=async_get_access_token,
|
||||
should_expose=config[CONF_FILTER],
|
||||
entity_config=config.get(CONF_ENTITY_CONFIG),
|
||||
)
|
||||
smart_home_config = AlexaConfig(hass, config)
|
||||
hass.http.register_view(SmartHomeView(smart_home_config))
|
||||
|
||||
if AUTH_KEY in hass.data:
|
||||
if smart_home_config.supports_auth:
|
||||
await async_enable_proactive_mode(hass, smart_home_config)
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ CONFIG_SCHEMA = vol.Schema({
|
|||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_MODE, default=DEFAULT_MODE):
|
||||
vol.In([MODE_DEV, MODE_PROD]),
|
||||
# Change to optional when we include real servers
|
||||
vol.Optional(CONF_COGNITO_CLIENT_ID): str,
|
||||
vol.Optional(CONF_USER_POOL_ID): str,
|
||||
vol.Optional(CONF_REGION): str,
|
||||
|
|
|
@ -26,6 +26,38 @@ from .const import (
|
|||
from .prefs import CloudPreferences
|
||||
|
||||
|
||||
class AlexaConfig(alexa_config.AbstractConfig):
|
||||
"""Alexa Configuration."""
|
||||
|
||||
def __init__(self, config, prefs):
|
||||
"""Initialize the Alexa config."""
|
||||
self._config = config
|
||||
self._prefs = prefs
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
"""Endpoint for report state."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def entity_config(self):
|
||||
"""Return entity config."""
|
||||
return self._config.get(CONF_ENTITY_CONFIG, {})
|
||||
|
||||
def should_expose(self, entity_id):
|
||||
"""If an entity should be exposed."""
|
||||
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
return False
|
||||
|
||||
if not self._config[CONF_FILTER].empty_filter:
|
||||
return self._config[CONF_FILTER](entity_id)
|
||||
|
||||
entity_configs = self._prefs.alexa_entity_configs
|
||||
entity_config = entity_configs.get(entity_id, {})
|
||||
return entity_config.get(
|
||||
PREF_SHOULD_EXPOSE, DEFAULT_SHOULD_EXPOSE)
|
||||
|
||||
|
||||
class CloudClient(Interface):
|
||||
"""Interface class for Home Assistant Cloud."""
|
||||
|
||||
|
@ -36,10 +68,10 @@ class CloudClient(Interface):
|
|||
self._hass = hass
|
||||
self._prefs = prefs
|
||||
self._websession = websession
|
||||
self._alexa_user_config = alexa_cfg
|
||||
self._google_user_config = google_config
|
||||
self.google_user_config = google_config
|
||||
self.alexa_user_config = alexa_cfg
|
||||
|
||||
self._alexa_config = None
|
||||
self.alexa_config = AlexaConfig(alexa_cfg, prefs)
|
||||
self._google_config = None
|
||||
|
||||
@property
|
||||
|
@ -77,26 +109,11 @@ class CloudClient(Interface):
|
|||
"""Return true if we want start a remote connection."""
|
||||
return self._prefs.remote_enabled
|
||||
|
||||
@property
|
||||
def alexa_config(self) -> alexa_config.Config:
|
||||
"""Return Alexa config."""
|
||||
if not self._alexa_config:
|
||||
alexa_conf = self._alexa_user_config
|
||||
|
||||
self._alexa_config = alexa_config.Config(
|
||||
endpoint=None,
|
||||
async_get_access_token=None,
|
||||
should_expose=alexa_conf[CONF_FILTER],
|
||||
entity_config=alexa_conf.get(CONF_ENTITY_CONFIG),
|
||||
)
|
||||
|
||||
return self._alexa_config
|
||||
|
||||
@property
|
||||
def google_config(self) -> ga_h.Config:
|
||||
"""Return Google config."""
|
||||
if not self._google_config:
|
||||
google_conf = self._google_user_config
|
||||
google_conf = self.google_user_config
|
||||
|
||||
def should_expose(entity):
|
||||
"""If an entity should be exposed."""
|
||||
|
@ -134,14 +151,8 @@ class CloudClient(Interface):
|
|||
|
||||
return self._google_config
|
||||
|
||||
@property
|
||||
def google_user_config(self) -> Dict[str, Any]:
|
||||
"""Return google action user config."""
|
||||
return self._google_user_config
|
||||
|
||||
async def cleanups(self) -> None:
|
||||
"""Cleanup some stuff after logout."""
|
||||
self._alexa_config = None
|
||||
self._google_config = None
|
||||
|
||||
@callback
|
||||
|
|
|
@ -9,6 +9,7 @@ PREF_GOOGLE_SECURE_DEVICES_PIN = 'google_secure_devices_pin'
|
|||
PREF_CLOUDHOOKS = 'cloudhooks'
|
||||
PREF_CLOUD_USER = 'cloud_user'
|
||||
PREF_GOOGLE_ENTITY_CONFIGS = 'google_entity_configs'
|
||||
PREF_ALEXA_ENTITY_CONFIGS = 'alexa_entity_configs'
|
||||
PREF_OVERRIDE_NAME = 'override_name'
|
||||
PREF_DISABLE_2FA = 'disable_2fa'
|
||||
PREF_ALIASES = 'aliases'
|
||||
|
|
|
@ -90,6 +90,9 @@ async def async_setup(hass):
|
|||
hass.components.websocket_api.async_register_command(
|
||||
google_assistant_update)
|
||||
|
||||
hass.components.websocket_api.async_register_command(alexa_list)
|
||||
hass.components.websocket_api.async_register_command(alexa_update)
|
||||
|
||||
hass.http.register_view(GoogleActionsSyncView)
|
||||
hass.http.register_view(CloudLoginView)
|
||||
hass.http.register_view(CloudLogoutView)
|
||||
|
@ -420,7 +423,7 @@ def _account_data(cloud):
|
|||
'cloud': cloud.iot.state,
|
||||
'prefs': client.prefs.as_dict(),
|
||||
'google_entities': client.google_user_config['filter'].config,
|
||||
'alexa_entities': client.alexa_config.should_expose.config,
|
||||
'alexa_entities': client.alexa_user_config['filter'].config,
|
||||
'alexa_domains': list(alexa_entities.ENTITY_ADAPTERS),
|
||||
'remote_domain': remote.instance_domain,
|
||||
'remote_connected': remote.is_connected,
|
||||
|
@ -508,3 +511,52 @@ async def google_assistant_update(hass, connection, msg):
|
|||
connection.send_result(
|
||||
msg['id'],
|
||||
cloud.client.prefs.google_entity_configs.get(msg['entity_id']))
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@_require_cloud_login
|
||||
@websocket_api.async_response
|
||||
@_ws_handle_cloud_errors
|
||||
@websocket_api.websocket_command({
|
||||
'type': 'cloud/alexa/entities'
|
||||
})
|
||||
async def alexa_list(hass, connection, msg):
|
||||
"""List all alexa entities."""
|
||||
cloud = hass.data[DOMAIN]
|
||||
entities = alexa_entities.async_get_entities(
|
||||
hass, cloud.client.alexa_config
|
||||
)
|
||||
|
||||
result = []
|
||||
|
||||
for entity in entities:
|
||||
result.append({
|
||||
'entity_id': entity.entity_id,
|
||||
'display_categories': entity.default_display_categories(),
|
||||
'interfaces': [ifc.name() for ifc in entity.interfaces()],
|
||||
})
|
||||
|
||||
connection.send_result(msg['id'], result)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@_require_cloud_login
|
||||
@websocket_api.async_response
|
||||
@_ws_handle_cloud_errors
|
||||
@websocket_api.websocket_command({
|
||||
'type': 'cloud/alexa/entities/update',
|
||||
'entity_id': str,
|
||||
vol.Optional('should_expose'): bool,
|
||||
})
|
||||
async def alexa_update(hass, connection, msg):
|
||||
"""Update alexa entity config."""
|
||||
cloud = hass.data[DOMAIN]
|
||||
changes = dict(msg)
|
||||
changes.pop('type')
|
||||
changes.pop('id')
|
||||
|
||||
await cloud.client.prefs.async_update_alexa_entity_config(**changes)
|
||||
|
||||
connection.send_result(
|
||||
msg['id'],
|
||||
cloud.client.prefs.alexa_entity_configs.get(msg['entity_id']))
|
||||
|
|
|
@ -5,7 +5,7 @@ from .const import (
|
|||
DOMAIN, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, PREF_ENABLE_REMOTE,
|
||||
PREF_GOOGLE_SECURE_DEVICES_PIN, PREF_CLOUDHOOKS, PREF_CLOUD_USER,
|
||||
PREF_GOOGLE_ENTITY_CONFIGS, PREF_OVERRIDE_NAME, PREF_DISABLE_2FA,
|
||||
PREF_ALIASES, PREF_SHOULD_EXPOSE,
|
||||
PREF_ALIASES, PREF_SHOULD_EXPOSE, PREF_ALEXA_ENTITY_CONFIGS,
|
||||
InvalidTrustedNetworks, InvalidTrustedProxies)
|
||||
|
||||
STORAGE_KEY = DOMAIN
|
||||
|
@ -33,6 +33,7 @@ class CloudPreferences:
|
|||
PREF_ENABLE_REMOTE: False,
|
||||
PREF_GOOGLE_SECURE_DEVICES_PIN: None,
|
||||
PREF_GOOGLE_ENTITY_CONFIGS: {},
|
||||
PREF_ALEXA_ENTITY_CONFIGS: {},
|
||||
PREF_CLOUDHOOKS: {},
|
||||
PREF_CLOUD_USER: None,
|
||||
}
|
||||
|
@ -42,7 +43,8 @@ class CloudPreferences:
|
|||
async def async_update(self, *, google_enabled=_UNDEF,
|
||||
alexa_enabled=_UNDEF, remote_enabled=_UNDEF,
|
||||
google_secure_devices_pin=_UNDEF, cloudhooks=_UNDEF,
|
||||
cloud_user=_UNDEF, google_entity_configs=_UNDEF):
|
||||
cloud_user=_UNDEF, google_entity_configs=_UNDEF,
|
||||
alexa_entity_configs=_UNDEF):
|
||||
"""Update user preferences."""
|
||||
for key, value in (
|
||||
(PREF_ENABLE_GOOGLE, google_enabled),
|
||||
|
@ -52,6 +54,7 @@ class CloudPreferences:
|
|||
(PREF_CLOUDHOOKS, cloudhooks),
|
||||
(PREF_CLOUD_USER, cloud_user),
|
||||
(PREF_GOOGLE_ENTITY_CONFIGS, google_entity_configs),
|
||||
(PREF_ALEXA_ENTITY_CONFIGS, alexa_entity_configs),
|
||||
):
|
||||
if value is not _UNDEF:
|
||||
self._prefs[key] = value
|
||||
|
@ -95,6 +98,33 @@ class CloudPreferences:
|
|||
}
|
||||
await self.async_update(google_entity_configs=updated_entities)
|
||||
|
||||
async def async_update_alexa_entity_config(
|
||||
self, *, entity_id, should_expose=_UNDEF):
|
||||
"""Update config for an Alexa entity."""
|
||||
entities = self.alexa_entity_configs
|
||||
entity = entities.get(entity_id, {})
|
||||
|
||||
changes = {}
|
||||
for key, value in (
|
||||
(PREF_SHOULD_EXPOSE, should_expose),
|
||||
):
|
||||
if value is not _UNDEF:
|
||||
changes[key] = value
|
||||
|
||||
if not changes:
|
||||
return
|
||||
|
||||
updated_entity = {
|
||||
**entity,
|
||||
**changes,
|
||||
}
|
||||
|
||||
updated_entities = {
|
||||
**entities,
|
||||
entity_id: updated_entity,
|
||||
}
|
||||
await self.async_update(alexa_entity_configs=updated_entities)
|
||||
|
||||
def as_dict(self):
|
||||
"""Return dictionary version."""
|
||||
return {
|
||||
|
@ -103,6 +133,7 @@ class CloudPreferences:
|
|||
PREF_ENABLE_REMOTE: self.remote_enabled,
|
||||
PREF_GOOGLE_SECURE_DEVICES_PIN: self.google_secure_devices_pin,
|
||||
PREF_GOOGLE_ENTITY_CONFIGS: self.google_entity_configs,
|
||||
PREF_ALEXA_ENTITY_CONFIGS: self.alexa_entity_configs,
|
||||
PREF_CLOUDHOOKS: self.cloudhooks,
|
||||
PREF_CLOUD_USER: self.cloud_user,
|
||||
}
|
||||
|
@ -140,6 +171,11 @@ class CloudPreferences:
|
|||
"""Return Google Entity configurations."""
|
||||
return self._prefs.get(PREF_GOOGLE_ENTITY_CONFIGS, {})
|
||||
|
||||
@property
|
||||
def alexa_entity_configs(self):
|
||||
"""Return Alexa Entity configurations."""
|
||||
return self._prefs.get(PREF_ALEXA_ENTITY_CONFIGS, {})
|
||||
|
||||
@property
|
||||
def cloudhooks(self):
|
||||
"""Return the published cloud webhooks."""
|
||||
|
|
|
@ -10,15 +10,35 @@ TEST_URL = "https://api.amazonalexa.com/v3/events"
|
|||
TEST_TOKEN_URL = "https://api.amazon.com/auth/o2/token"
|
||||
|
||||
|
||||
async def get_access_token():
|
||||
"""Return a test access token."""
|
||||
return "thisisnotanacesstoken"
|
||||
class MockConfig(config.AbstractConfig):
|
||||
"""Mock Alexa config."""
|
||||
|
||||
entity_config = {}
|
||||
|
||||
@property
|
||||
def supports_auth(self):
|
||||
"""Return if config supports auth."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
"""Endpoint for report state."""
|
||||
return TEST_URL
|
||||
|
||||
def should_expose(self, entity_id):
|
||||
"""If an entity should be exposed."""
|
||||
return True
|
||||
|
||||
async def async_get_access_token(self):
|
||||
"""Get an access token."""
|
||||
return "thisisnotanacesstoken"
|
||||
|
||||
async def async_accept_grant(self, code):
|
||||
"""Accept a grant."""
|
||||
pass
|
||||
|
||||
|
||||
DEFAULT_CONFIG = config.Config(
|
||||
endpoint=TEST_URL,
|
||||
async_get_access_token=get_access_token,
|
||||
should_expose=lambda entity_id: True)
|
||||
DEFAULT_CONFIG = MockConfig()
|
||||
|
||||
|
||||
def get_new_request(namespace, name, endpoint=None):
|
||||
|
|
|
@ -4,7 +4,6 @@ import pytest
|
|||
from homeassistant.core import Context, callback
|
||||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.components.alexa import (
|
||||
config,
|
||||
smart_home,
|
||||
messages,
|
||||
)
|
||||
|
@ -14,6 +13,7 @@ from tests.common import async_mock_service
|
|||
|
||||
from . import (
|
||||
get_new_request,
|
||||
MockConfig,
|
||||
DEFAULT_CONFIG,
|
||||
assert_request_calls_service,
|
||||
assert_request_fails,
|
||||
|
@ -1012,15 +1012,13 @@ async def test_exclude_filters(hass):
|
|||
hass.states.async_set(
|
||||
'cover.deny', 'off', {'friendly_name': "Blocked cover"})
|
||||
|
||||
alexa_config = config.Config(
|
||||
endpoint=None,
|
||||
async_get_access_token=None,
|
||||
should_expose=entityfilter.generate_filter(
|
||||
include_domains=[],
|
||||
include_entities=[],
|
||||
exclude_domains=['script'],
|
||||
exclude_entities=['cover.deny'],
|
||||
))
|
||||
alexa_config = MockConfig()
|
||||
alexa_config.should_expose = entityfilter.generate_filter(
|
||||
include_domains=[],
|
||||
include_entities=[],
|
||||
exclude_domains=['script'],
|
||||
exclude_entities=['cover.deny'],
|
||||
)
|
||||
|
||||
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1047,15 +1045,13 @@ async def test_include_filters(hass):
|
|||
hass.states.async_set(
|
||||
'group.allow', 'off', {'friendly_name': "Allowed group"})
|
||||
|
||||
alexa_config = config.Config(
|
||||
endpoint=None,
|
||||
async_get_access_token=None,
|
||||
should_expose=entityfilter.generate_filter(
|
||||
include_domains=['automation', 'group'],
|
||||
include_entities=['script.deny'],
|
||||
exclude_domains=[],
|
||||
exclude_entities=[],
|
||||
))
|
||||
alexa_config = MockConfig()
|
||||
alexa_config.should_expose = entityfilter.generate_filter(
|
||||
include_domains=['automation', 'group'],
|
||||
include_entities=['script.deny'],
|
||||
exclude_domains=[],
|
||||
exclude_entities=[],
|
||||
)
|
||||
|
||||
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1076,15 +1072,13 @@ async def test_never_exposed_entities(hass):
|
|||
hass.states.async_set(
|
||||
'group.allow', 'off', {'friendly_name': "Allowed group"})
|
||||
|
||||
alexa_config = config.Config(
|
||||
endpoint=None,
|
||||
async_get_access_token=None,
|
||||
should_expose=entityfilter.generate_filter(
|
||||
include_domains=['group'],
|
||||
include_entities=[],
|
||||
exclude_domains=[],
|
||||
exclude_entities=[],
|
||||
))
|
||||
alexa_config = MockConfig()
|
||||
alexa_config.should_expose = entityfilter.generate_filter(
|
||||
include_domains=['group'],
|
||||
include_entities=[],
|
||||
exclude_domains=[],
|
||||
exclude_entities=[],
|
||||
)
|
||||
|
||||
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1161,18 +1155,14 @@ async def test_entity_config(hass):
|
|||
hass.states.async_set(
|
||||
'light.test_1', 'on', {'friendly_name': "Test light 1"})
|
||||
|
||||
alexa_config = config.Config(
|
||||
endpoint=None,
|
||||
async_get_access_token=None,
|
||||
should_expose=lambda entity_id: True,
|
||||
entity_config={
|
||||
'light.test_1': {
|
||||
'name': 'Config name',
|
||||
'display_categories': 'SWITCH',
|
||||
'description': 'Config description'
|
||||
}
|
||||
alexa_config = MockConfig()
|
||||
alexa_config.entity_config = {
|
||||
'light.test_1': {
|
||||
'name': 'Config name',
|
||||
'display_categories': 'SWITCH',
|
||||
'description': 'Config description'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
msg = await smart_home.async_handle_message(
|
||||
hass, alexa_config, request)
|
||||
|
|
|
@ -7,7 +7,8 @@ import pytest
|
|||
|
||||
from homeassistant.core import State
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.cloud import DOMAIN
|
||||
from homeassistant.components.cloud import (
|
||||
DOMAIN, ALEXA_SCHEMA, prefs, client)
|
||||
from homeassistant.components.cloud.const import (
|
||||
PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE)
|
||||
from tests.components.alexa import test_smart_home as test_alexa
|
||||
|
@ -251,3 +252,20 @@ async def test_google_config_should_2fa(
|
|||
)
|
||||
|
||||
assert not cloud_client.google_config.should_2fa(state)
|
||||
|
||||
|
||||
async def test_alexa_config_expose_entity_prefs(hass):
|
||||
"""Test Alexa config should expose using prefs."""
|
||||
cloud_prefs = prefs.CloudPreferences(hass)
|
||||
await cloud_prefs.async_initialize()
|
||||
entity_conf = {
|
||||
'should_expose': False
|
||||
}
|
||||
await cloud_prefs.async_update(alexa_entity_configs={
|
||||
'light.kitchen': entity_conf
|
||||
})
|
||||
conf = client.AlexaConfig(ALEXA_SCHEMA({}), cloud_prefs)
|
||||
|
||||
assert not conf.should_expose('light.kitchen')
|
||||
entity_conf['should_expose'] = True
|
||||
assert conf.should_expose('light.kitchen')
|
||||
|
|
|
@ -15,6 +15,7 @@ from homeassistant.components.cloud.const import (
|
|||
DOMAIN)
|
||||
from homeassistant.components.google_assistant.helpers import (
|
||||
GoogleEntity, Config)
|
||||
from homeassistant.components.alexa.entities import LightCapabilities
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
@ -361,6 +362,7 @@ async def test_websocket_status(hass, hass_ws_client, mock_cloud_fixture,
|
|||
'google_enabled': True,
|
||||
'google_entity_configs': {},
|
||||
'google_secure_devices_pin': None,
|
||||
'alexa_entity_configs': {},
|
||||
'remote_enabled': False,
|
||||
},
|
||||
'alexa_entities': {
|
||||
|
@ -800,3 +802,46 @@ async def test_enabling_remote_trusted_proxies_local6(
|
|||
'Remote UI not compatible with 127.0.0.1/::1 as trusted proxies.'
|
||||
|
||||
assert len(mock_connect.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_list_alexa_entities(
|
||||
hass, hass_ws_client, setup_api, mock_cloud_login):
|
||||
"""Test that we can list Alexa entities."""
|
||||
client = await hass_ws_client(hass)
|
||||
entity = LightCapabilities(hass, MagicMock(entity_config={}), State(
|
||||
'light.kitchen', 'on'
|
||||
))
|
||||
with patch('homeassistant.components.alexa.entities'
|
||||
'.async_get_entities', return_value=[entity]):
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/alexa/entities',
|
||||
})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert response['success']
|
||||
assert len(response['result']) == 1
|
||||
assert response['result'][0] == {
|
||||
'entity_id': 'light.kitchen',
|
||||
'display_categories': ['LIGHT'],
|
||||
'interfaces': ['Alexa.PowerController', 'Alexa.EndpointHealth'],
|
||||
}
|
||||
|
||||
|
||||
async def test_update_alexa_entity(
|
||||
hass, hass_ws_client, setup_api, mock_cloud_login):
|
||||
"""Test that we can update config of an Alexa entity."""
|
||||
client = await hass_ws_client(hass)
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/alexa/entities/update',
|
||||
'entity_id': 'light.kitchen',
|
||||
'should_expose': False,
|
||||
})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert response['success']
|
||||
prefs = hass.data[DOMAIN].client.prefs
|
||||
assert prefs.alexa_entity_configs['light.kitchen'] == {
|
||||
'should_expose': False,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue