commit
2e9c71f2c0
5 changed files with 188 additions and 159 deletions
|
@ -78,7 +78,7 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
@property
|
@property
|
||||||
def entity_config(self):
|
def entity_config(self):
|
||||||
"""Return entity config."""
|
"""Return entity config."""
|
||||||
return self._config.get(CONF_ENTITY_CONFIG, {})
|
return self._config.get(CONF_ENTITY_CONFIG) or {}
|
||||||
|
|
||||||
def should_expose(self, entity_id):
|
def should_expose(self, entity_id):
|
||||||
"""If an entity should be exposed."""
|
"""If an entity should be exposed."""
|
||||||
|
@ -129,6 +129,11 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
else:
|
else:
|
||||||
await self.async_disable_proactive_mode()
|
await self.async_disable_proactive_mode()
|
||||||
|
|
||||||
|
# State reporting is reported as a property on entities.
|
||||||
|
# So when we change it, we need to sync all entities.
|
||||||
|
await self.async_sync_entities()
|
||||||
|
return
|
||||||
|
|
||||||
# If entity prefs are the same or we have filter in config.yaml,
|
# If entity prefs are the same or we have filter in config.yaml,
|
||||||
# don't sync.
|
# don't sync.
|
||||||
if (self._cur_entity_prefs is prefs.alexa_entity_configs or
|
if (self._cur_entity_prefs is prefs.alexa_entity_configs or
|
||||||
|
@ -190,6 +195,11 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
|
|
||||||
async def async_sync_entities(self):
|
async def async_sync_entities(self):
|
||||||
"""Sync all entities to Alexa."""
|
"""Sync all entities to Alexa."""
|
||||||
|
# Remove any pending sync
|
||||||
|
if self._alexa_sync_unsub:
|
||||||
|
self._alexa_sync_unsub()
|
||||||
|
self._alexa_sync_unsub = None
|
||||||
|
|
||||||
to_update = []
|
to_update = []
|
||||||
to_remove = []
|
to_remove = []
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 95
|
MINOR_VERSION = 95
|
||||||
PATCH_VERSION = '2'
|
PATCH_VERSION = '3'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
REQUIRED_PYTHON_VER = (3, 5, 3)
|
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
"""Fixtures for cloud tests."""
|
"""Fixtures for cloud tests."""
|
||||||
import pytest
|
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.components.cloud import prefs
|
import jwt
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.cloud import const, prefs
|
||||||
|
|
||||||
from . import mock_cloud, mock_cloud_prefs
|
from . import mock_cloud, mock_cloud_prefs
|
||||||
|
|
||||||
|
@ -28,3 +29,19 @@ async def cloud_prefs(hass):
|
||||||
cloud_prefs = prefs.CloudPreferences(hass)
|
cloud_prefs = prefs.CloudPreferences(hass)
|
||||||
await cloud_prefs.async_initialize()
|
await cloud_prefs.async_initialize()
|
||||||
return cloud_prefs
|
return cloud_prefs
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def mock_cloud_setup(hass):
|
||||||
|
"""Set up the cloud."""
|
||||||
|
await mock_cloud(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_cloud_login(hass, mock_cloud_setup):
|
||||||
|
"""Mock cloud is logged in."""
|
||||||
|
hass.data[const.DOMAIN].id_token = jwt.encode({
|
||||||
|
'email': 'hello@home-assistant.io',
|
||||||
|
'custom:sub-exp': '2018-01-03',
|
||||||
|
'cognito:username': 'abcdefghjkl',
|
||||||
|
}, 'test')
|
||||||
|
|
154
tests/components/cloud/test_alexa_config.py
Normal file
154
tests/components/cloud/test_alexa_config.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
"""Test Alexa config."""
|
||||||
|
import contextlib
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components.cloud import ALEXA_SCHEMA, alexa_config
|
||||||
|
from homeassistant.util.dt import utcnow
|
||||||
|
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
||||||
|
from tests.common import mock_coro, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs):
|
||||||
|
"""Test Alexa config should expose using prefs."""
|
||||||
|
entity_conf = {
|
||||||
|
'should_expose': False
|
||||||
|
}
|
||||||
|
await cloud_prefs.async_update(alexa_entity_configs={
|
||||||
|
'light.kitchen': entity_conf
|
||||||
|
})
|
||||||
|
conf = alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
||||||
|
|
||||||
|
assert not conf.should_expose('light.kitchen')
|
||||||
|
entity_conf['should_expose'] = True
|
||||||
|
assert conf.should_expose('light.kitchen')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alexa_config_report_state(hass, cloud_prefs):
|
||||||
|
"""Test Alexa config should expose using prefs."""
|
||||||
|
conf = alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
||||||
|
|
||||||
|
assert cloud_prefs.alexa_report_state is False
|
||||||
|
assert conf.should_report_state is False
|
||||||
|
assert conf.is_reporting_states is False
|
||||||
|
|
||||||
|
with patch.object(conf, 'async_get_access_token',
|
||||||
|
return_value=mock_coro("hello")):
|
||||||
|
await cloud_prefs.async_update(alexa_report_state=True)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert cloud_prefs.alexa_report_state is True
|
||||||
|
assert conf.should_report_state is True
|
||||||
|
assert conf.is_reporting_states is True
|
||||||
|
|
||||||
|
await cloud_prefs.async_update(alexa_report_state=False)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert cloud_prefs.alexa_report_state is False
|
||||||
|
assert conf.should_report_state is False
|
||||||
|
assert conf.is_reporting_states is False
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def patch_sync_helper():
|
||||||
|
"""Patch sync helper.
|
||||||
|
|
||||||
|
In Py3.7 this would have been an async context manager.
|
||||||
|
"""
|
||||||
|
to_update = []
|
||||||
|
to_remove = []
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
'homeassistant.components.cloud.alexa_config.SYNC_DELAY', 0
|
||||||
|
), patch(
|
||||||
|
'homeassistant.components.cloud.alexa_config.AlexaConfig._sync_helper',
|
||||||
|
side_effect=mock_coro
|
||||||
|
) as mock_helper:
|
||||||
|
yield to_update, to_remove
|
||||||
|
|
||||||
|
actual_to_update, actual_to_remove = mock_helper.mock_calls[0][1]
|
||||||
|
to_update.extend(actual_to_update)
|
||||||
|
to_remove.extend(actual_to_remove)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alexa_update_expose_trigger_sync(hass, cloud_prefs):
|
||||||
|
"""Test Alexa config responds to updating exposed entities."""
|
||||||
|
alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
||||||
|
|
||||||
|
with patch_sync_helper() as (to_update, to_remove):
|
||||||
|
await cloud_prefs.async_update_alexa_entity_config(
|
||||||
|
entity_id='light.kitchen', should_expose=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
async_fire_time_changed(hass, utcnow())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert to_update == ['light.kitchen']
|
||||||
|
assert to_remove == []
|
||||||
|
|
||||||
|
with patch_sync_helper() as (to_update, to_remove):
|
||||||
|
await cloud_prefs.async_update_alexa_entity_config(
|
||||||
|
entity_id='light.kitchen', should_expose=False
|
||||||
|
)
|
||||||
|
await cloud_prefs.async_update_alexa_entity_config(
|
||||||
|
entity_id='binary_sensor.door', should_expose=True
|
||||||
|
)
|
||||||
|
await cloud_prefs.async_update_alexa_entity_config(
|
||||||
|
entity_id='sensor.temp', should_expose=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
async_fire_time_changed(hass, utcnow())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert sorted(to_update) == ['binary_sensor.door', 'sensor.temp']
|
||||||
|
assert to_remove == ['light.kitchen']
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alexa_entity_registry_sync(hass, mock_cloud_login, cloud_prefs):
|
||||||
|
"""Test Alexa config responds to entity registry."""
|
||||||
|
alexa_config.AlexaConfig(
|
||||||
|
hass, ALEXA_SCHEMA({}), cloud_prefs, hass.data['cloud'])
|
||||||
|
|
||||||
|
with patch_sync_helper() as (to_update, to_remove):
|
||||||
|
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
||||||
|
'action': 'create',
|
||||||
|
'entity_id': 'light.kitchen',
|
||||||
|
})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert to_update == ['light.kitchen']
|
||||||
|
assert to_remove == []
|
||||||
|
|
||||||
|
with patch_sync_helper() as (to_update, to_remove):
|
||||||
|
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
||||||
|
'action': 'remove',
|
||||||
|
'entity_id': 'light.kitchen',
|
||||||
|
})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert to_update == []
|
||||||
|
assert to_remove == ['light.kitchen']
|
||||||
|
|
||||||
|
with patch_sync_helper() as (to_update, to_remove):
|
||||||
|
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
||||||
|
'action': 'update',
|
||||||
|
'entity_id': 'light.kitchen',
|
||||||
|
})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert to_update == []
|
||||||
|
assert to_remove == []
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alexa_update_report_state(hass, cloud_prefs):
|
||||||
|
"""Test Alexa config responds to reporting state."""
|
||||||
|
alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
'homeassistant.components.cloud.alexa_config.AlexaConfig.'
|
||||||
|
'async_sync_entities', side_effect=mock_coro) as mock_sync, patch(
|
||||||
|
'homeassistant.components.cloud.alexa_config.'
|
||||||
|
'AlexaConfig.async_enable_proactive_mode', side_effect=mock_coro):
|
||||||
|
await cloud_prefs.async_update(alexa_report_state=True)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(mock_sync.mock_calls) == 1
|
|
@ -1,21 +1,16 @@
|
||||||
"""Test the cloud.iot module."""
|
"""Test the cloud.iot module."""
|
||||||
import contextlib
|
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import jwt
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.components.cloud import (
|
from homeassistant.components.cloud import DOMAIN
|
||||||
DOMAIN, ALEXA_SCHEMA, alexa_config)
|
|
||||||
from homeassistant.components.cloud.const import (
|
from homeassistant.components.cloud.const import (
|
||||||
PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE)
|
PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE)
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
|
||||||
from tests.components.alexa import test_smart_home as test_alexa
|
from tests.components.alexa import test_smart_home as test_alexa
|
||||||
from tests.common import mock_coro, async_fire_time_changed
|
from tests.common import mock_coro
|
||||||
|
|
||||||
from . import mock_cloud_prefs, mock_cloud
|
from . import mock_cloud_prefs, mock_cloud
|
||||||
|
|
||||||
|
@ -26,22 +21,6 @@ def mock_cloud_inst():
|
||||||
return MagicMock(subscription_expired=False)
|
return MagicMock(subscription_expired=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def mock_cloud_setup(hass):
|
|
||||||
"""Set up the cloud."""
|
|
||||||
await mock_cloud(hass)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_cloud_login(hass, mock_cloud_setup):
|
|
||||||
"""Mock cloud is logged in."""
|
|
||||||
hass.data[DOMAIN].id_token = jwt.encode({
|
|
||||||
'email': 'hello@home-assistant.io',
|
|
||||||
'custom:sub-exp': '2018-01-03',
|
|
||||||
'cognito:username': 'abcdefghjkl',
|
|
||||||
}, 'test')
|
|
||||||
|
|
||||||
|
|
||||||
async def test_handler_alexa(hass):
|
async def test_handler_alexa(hass):
|
||||||
"""Test handler Alexa."""
|
"""Test handler Alexa."""
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
|
@ -244,134 +223,3 @@ async def test_google_config_should_2fa(
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not cloud_client.google_config.should_2fa(state)
|
assert not cloud_client.google_config.should_2fa(state)
|
||||||
|
|
||||||
|
|
||||||
async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs):
|
|
||||||
"""Test Alexa config should expose using prefs."""
|
|
||||||
entity_conf = {
|
|
||||||
'should_expose': False
|
|
||||||
}
|
|
||||||
await cloud_prefs.async_update(alexa_entity_configs={
|
|
||||||
'light.kitchen': entity_conf
|
|
||||||
})
|
|
||||||
conf = alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
|
||||||
|
|
||||||
assert not conf.should_expose('light.kitchen')
|
|
||||||
entity_conf['should_expose'] = True
|
|
||||||
assert conf.should_expose('light.kitchen')
|
|
||||||
|
|
||||||
|
|
||||||
async def test_alexa_config_report_state(hass, cloud_prefs):
|
|
||||||
"""Test Alexa config should expose using prefs."""
|
|
||||||
conf = alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
|
||||||
|
|
||||||
assert cloud_prefs.alexa_report_state is False
|
|
||||||
assert conf.should_report_state is False
|
|
||||||
assert conf.is_reporting_states is False
|
|
||||||
|
|
||||||
with patch.object(conf, 'async_get_access_token',
|
|
||||||
return_value=mock_coro("hello")):
|
|
||||||
await cloud_prefs.async_update(alexa_report_state=True)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert cloud_prefs.alexa_report_state is True
|
|
||||||
assert conf.should_report_state is True
|
|
||||||
assert conf.is_reporting_states is True
|
|
||||||
|
|
||||||
await cloud_prefs.async_update(alexa_report_state=False)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert cloud_prefs.alexa_report_state is False
|
|
||||||
assert conf.should_report_state is False
|
|
||||||
assert conf.is_reporting_states is False
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def patch_sync_helper():
|
|
||||||
"""Patch sync helper.
|
|
||||||
|
|
||||||
In Py3.7 this would have been an async context manager.
|
|
||||||
"""
|
|
||||||
to_update = []
|
|
||||||
to_remove = []
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
'homeassistant.components.cloud.alexa_config.SYNC_DELAY', 0
|
|
||||||
), patch(
|
|
||||||
'homeassistant.components.cloud.alexa_config.AlexaConfig._sync_helper',
|
|
||||||
side_effect=mock_coro
|
|
||||||
) as mock_helper:
|
|
||||||
yield to_update, to_remove
|
|
||||||
|
|
||||||
actual_to_update, actual_to_remove = mock_helper.mock_calls[0][1]
|
|
||||||
to_update.extend(actual_to_update)
|
|
||||||
to_remove.extend(actual_to_remove)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_alexa_update_expose_trigger_sync(hass, cloud_prefs):
|
|
||||||
"""Test Alexa config responds to updating exposed entities."""
|
|
||||||
alexa_config.AlexaConfig(hass, ALEXA_SCHEMA({}), cloud_prefs, None)
|
|
||||||
|
|
||||||
with patch_sync_helper() as (to_update, to_remove):
|
|
||||||
await cloud_prefs.async_update_alexa_entity_config(
|
|
||||||
entity_id='light.kitchen', should_expose=True
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
async_fire_time_changed(hass, utcnow())
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert to_update == ['light.kitchen']
|
|
||||||
assert to_remove == []
|
|
||||||
|
|
||||||
with patch_sync_helper() as (to_update, to_remove):
|
|
||||||
await cloud_prefs.async_update_alexa_entity_config(
|
|
||||||
entity_id='light.kitchen', should_expose=False
|
|
||||||
)
|
|
||||||
await cloud_prefs.async_update_alexa_entity_config(
|
|
||||||
entity_id='binary_sensor.door', should_expose=True
|
|
||||||
)
|
|
||||||
await cloud_prefs.async_update_alexa_entity_config(
|
|
||||||
entity_id='sensor.temp', should_expose=True
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
async_fire_time_changed(hass, utcnow())
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert sorted(to_update) == ['binary_sensor.door', 'sensor.temp']
|
|
||||||
assert to_remove == ['light.kitchen']
|
|
||||||
|
|
||||||
|
|
||||||
async def test_alexa_entity_registry_sync(hass, mock_cloud_login, cloud_prefs):
|
|
||||||
"""Test Alexa config responds to entity registry."""
|
|
||||||
alexa_config.AlexaConfig(
|
|
||||||
hass, ALEXA_SCHEMA({}), cloud_prefs, hass.data['cloud'])
|
|
||||||
|
|
||||||
with patch_sync_helper() as (to_update, to_remove):
|
|
||||||
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
|
||||||
'action': 'create',
|
|
||||||
'entity_id': 'light.kitchen',
|
|
||||||
})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert to_update == ['light.kitchen']
|
|
||||||
assert to_remove == []
|
|
||||||
|
|
||||||
with patch_sync_helper() as (to_update, to_remove):
|
|
||||||
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
|
||||||
'action': 'remove',
|
|
||||||
'entity_id': 'light.kitchen',
|
|
||||||
})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert to_update == []
|
|
||||||
assert to_remove == ['light.kitchen']
|
|
||||||
|
|
||||||
with patch_sync_helper() as (to_update, to_remove):
|
|
||||||
hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
|
|
||||||
'action': 'update',
|
|
||||||
'entity_id': 'light.kitchen',
|
|
||||||
})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert to_update == []
|
|
||||||
assert to_remove == []
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue