Don't expose config or diagnostic entities to cloud (#57771)
parent
8b33aa3702
commit
0f2b5ea28e
|
@ -15,9 +15,14 @@ from homeassistant.components.alexa import (
|
||||||
errors as alexa_errors,
|
errors as alexa_errors,
|
||||||
state_report as alexa_state_report,
|
state_report as alexa_state_report,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, HTTP_BAD_REQUEST
|
from homeassistant.const import (
|
||||||
|
CLOUD_NEVER_EXPOSED_ENTITIES,
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
HTTP_BAD_REQUEST,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
||||||
from homeassistant.helpers import entity_registry, start
|
from homeassistant.helpers import entity_registry as er, start
|
||||||
from homeassistant.helpers.event import async_call_later
|
from homeassistant.helpers.event import async_call_later
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
@ -110,7 +115,7 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
|
|
||||||
self._prefs.async_listen_updates(self._async_prefs_updated)
|
self._prefs.async_listen_updates(self._async_prefs_updated)
|
||||||
self.hass.bus.async_listen(
|
self.hass.bus.async_listen(
|
||||||
entity_registry.EVENT_ENTITY_REGISTRY_UPDATED,
|
er.EVENT_ENTITY_REGISTRY_UPDATED,
|
||||||
self._handle_entity_registry_updated,
|
self._handle_entity_registry_updated,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -128,13 +133,23 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
if entity_expose is not None:
|
if entity_expose is not None:
|
||||||
return entity_expose
|
return entity_expose
|
||||||
|
|
||||||
|
entity_registry = er.async_get(self.hass)
|
||||||
|
registry_entry = entity_registry.async_get(entity_id)
|
||||||
|
if registry_entry:
|
||||||
|
auxiliary_entity = registry_entry.entity_category in (
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
auxiliary_entity = False
|
||||||
|
|
||||||
default_expose = self._prefs.alexa_default_expose
|
default_expose = self._prefs.alexa_default_expose
|
||||||
|
|
||||||
# Backwards compat
|
# Backwards compat
|
||||||
if default_expose is None:
|
if default_expose is None:
|
||||||
return True
|
return not auxiliary_entity
|
||||||
|
|
||||||
return split_entity_id(entity_id)[0] in default_expose
|
return not auxiliary_entity and split_entity_id(entity_id)[0] in default_expose
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_invalidate_access_token(self):
|
def async_invalidate_access_token(self):
|
||||||
|
@ -340,7 +355,7 @@ class AlexaConfig(alexa_config.AbstractConfig):
|
||||||
elif action == "remove":
|
elif action == "remove":
|
||||||
to_remove.append(entity_id)
|
to_remove.append(entity_id)
|
||||||
elif action == "update" and bool(
|
elif action == "update" and bool(
|
||||||
set(event.data["changes"]) & entity_registry.ENTITY_DESCRIBING_ATTRIBUTES
|
set(event.data["changes"]) & er.ENTITY_DESCRIBING_ATTRIBUTES
|
||||||
):
|
):
|
||||||
to_update.append(entity_id)
|
to_update.append(entity_id)
|
||||||
if "old_entity_id" in event.data:
|
if "old_entity_id" in event.data:
|
||||||
|
|
|
@ -7,9 +7,14 @@ from hass_nabucasa.google_report_state import ErrorResponse
|
||||||
|
|
||||||
from homeassistant.components.google_assistant.const import DOMAIN as GOOGLE_DOMAIN
|
from homeassistant.components.google_assistant.const import DOMAIN as GOOGLE_DOMAIN
|
||||||
from homeassistant.components.google_assistant.helpers import AbstractConfig
|
from homeassistant.components.google_assistant.helpers import AbstractConfig
|
||||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, HTTP_OK
|
from homeassistant.const import (
|
||||||
|
CLOUD_NEVER_EXPOSED_ENTITIES,
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
HTTP_OK,
|
||||||
|
)
|
||||||
from homeassistant.core import CoreState, split_entity_id
|
from homeassistant.core import CoreState, split_entity_id
|
||||||
from homeassistant.helpers import entity_registry, start
|
from homeassistant.helpers import entity_registry as er, start
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -104,7 +109,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||||
self._prefs.async_listen_updates(self._async_prefs_updated)
|
self._prefs.async_listen_updates(self._async_prefs_updated)
|
||||||
|
|
||||||
self.hass.bus.async_listen(
|
self.hass.bus.async_listen(
|
||||||
entity_registry.EVENT_ENTITY_REGISTRY_UPDATED,
|
er.EVENT_ENTITY_REGISTRY_UPDATED,
|
||||||
self._handle_entity_registry_updated,
|
self._handle_entity_registry_updated,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -126,13 +131,23 @@ class CloudGoogleConfig(AbstractConfig):
|
||||||
if entity_expose is not None:
|
if entity_expose is not None:
|
||||||
return entity_expose
|
return entity_expose
|
||||||
|
|
||||||
|
entity_registry = er.async_get(self.hass)
|
||||||
|
registry_entry = entity_registry.async_get(entity_id)
|
||||||
|
if registry_entry:
|
||||||
|
auxiliary_entity = registry_entry.entity_category in (
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
auxiliary_entity = False
|
||||||
|
|
||||||
default_expose = self._prefs.google_default_expose
|
default_expose = self._prefs.google_default_expose
|
||||||
|
|
||||||
# Backwards compat
|
# Backwards compat
|
||||||
if default_expose is None:
|
if default_expose is None:
|
||||||
return True
|
return not auxiliary_entity
|
||||||
|
|
||||||
return split_entity_id(entity_id)[0] in default_expose
|
return not auxiliary_entity and split_entity_id(entity_id)[0] in default_expose
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agent_user_id(self):
|
def agent_user_id(self):
|
||||||
|
@ -215,7 +230,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||||
|
|
||||||
# Only consider entity registry updates if info relevant for Google has changed
|
# Only consider entity registry updates if info relevant for Google has changed
|
||||||
if event.data["action"] == "update" and not bool(
|
if event.data["action"] == "update" and not bool(
|
||||||
set(event.data["changes"]) & entity_registry.ENTITY_DESCRIBING_ATTRIBUTES
|
set(event.data["changes"]) & er.ENTITY_DESCRIBING_ATTRIBUTES
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.components.cloud import ALEXA_SCHEMA, alexa_config
|
||||||
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed, mock_registry
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
@ -19,6 +19,23 @@ def cloud_stub():
|
||||||
|
|
||||||
async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub):
|
async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub):
|
||||||
"""Test Alexa config should expose using prefs."""
|
"""Test Alexa config should expose using prefs."""
|
||||||
|
entity_registry = mock_registry(hass)
|
||||||
|
|
||||||
|
entity_entry1 = entity_registry.async_get_or_create(
|
||||||
|
"switch",
|
||||||
|
"test",
|
||||||
|
"switch_config_id",
|
||||||
|
suggested_object_id="config_switch",
|
||||||
|
entity_category="config",
|
||||||
|
)
|
||||||
|
entity_entry2 = entity_registry.async_get_or_create(
|
||||||
|
"switch",
|
||||||
|
"test",
|
||||||
|
"switch_diagnostic_id",
|
||||||
|
suggested_object_id="diagnostic_switch",
|
||||||
|
entity_category="diagnostic",
|
||||||
|
)
|
||||||
|
|
||||||
entity_conf = {"should_expose": False}
|
entity_conf = {"should_expose": False}
|
||||||
await cloud_prefs.async_update(
|
await cloud_prefs.async_update(
|
||||||
alexa_entity_configs={"light.kitchen": entity_conf},
|
alexa_entity_configs={"light.kitchen": entity_conf},
|
||||||
|
@ -31,11 +48,20 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub):
|
||||||
await conf.async_initialize()
|
await conf.async_initialize()
|
||||||
|
|
||||||
assert not conf.should_expose("light.kitchen")
|
assert not conf.should_expose("light.kitchen")
|
||||||
|
assert not conf.should_expose(entity_entry1.entity_id)
|
||||||
|
assert not conf.should_expose(entity_entry2.entity_id)
|
||||||
|
|
||||||
entity_conf["should_expose"] = True
|
entity_conf["should_expose"] = True
|
||||||
assert conf.should_expose("light.kitchen")
|
assert conf.should_expose("light.kitchen")
|
||||||
|
# config and diagnostic entities should not be exposed
|
||||||
|
assert not conf.should_expose(entity_entry1.entity_id)
|
||||||
|
assert not conf.should_expose(entity_entry2.entity_id)
|
||||||
|
|
||||||
entity_conf["should_expose"] = None
|
entity_conf["should_expose"] = None
|
||||||
assert conf.should_expose("light.kitchen")
|
assert conf.should_expose("light.kitchen")
|
||||||
|
# config and diagnostic entities should not be exposed
|
||||||
|
assert not conf.should_expose(entity_entry1.entity_id)
|
||||||
|
assert not conf.should_expose(entity_entry2.entity_id)
|
||||||
|
|
||||||
assert "alexa" not in hass.config.components
|
assert "alexa" not in hass.config.components
|
||||||
await cloud_prefs.async_update(
|
await cloud_prefs.async_update(
|
||||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.core import CoreState, State
|
||||||
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed, mock_registry
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -215,8 +215,25 @@ async def test_sync_google_on_home_assistant_start(hass, mock_cloud_login, cloud
|
||||||
assert len(mock_sync.mock_calls) == 1
|
assert len(mock_sync.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_google_config_expose_entity_prefs(mock_conf, cloud_prefs):
|
async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs):
|
||||||
"""Test Google config should expose using prefs."""
|
"""Test Google config should expose using prefs."""
|
||||||
|
entity_registry = mock_registry(hass)
|
||||||
|
|
||||||
|
entity_entry1 = entity_registry.async_get_or_create(
|
||||||
|
"switch",
|
||||||
|
"test",
|
||||||
|
"switch_config_id",
|
||||||
|
suggested_object_id="config_switch",
|
||||||
|
entity_category="config",
|
||||||
|
)
|
||||||
|
entity_entry2 = entity_registry.async_get_or_create(
|
||||||
|
"switch",
|
||||||
|
"test",
|
||||||
|
"switch_diagnostic_id",
|
||||||
|
suggested_object_id="diagnostic_switch",
|
||||||
|
entity_category="diagnostic",
|
||||||
|
)
|
||||||
|
|
||||||
entity_conf = {"should_expose": False}
|
entity_conf = {"should_expose": False}
|
||||||
await cloud_prefs.async_update(
|
await cloud_prefs.async_update(
|
||||||
google_entity_configs={"light.kitchen": entity_conf},
|
google_entity_configs={"light.kitchen": entity_conf},
|
||||||
|
@ -224,13 +241,24 @@ async def test_google_config_expose_entity_prefs(mock_conf, cloud_prefs):
|
||||||
)
|
)
|
||||||
|
|
||||||
state = State("light.kitchen", "on")
|
state = State("light.kitchen", "on")
|
||||||
|
state_config = State(entity_entry1.entity_id, "on")
|
||||||
|
state_diagnostic = State(entity_entry2.entity_id, "on")
|
||||||
|
|
||||||
assert not mock_conf.should_expose(state)
|
assert not mock_conf.should_expose(state)
|
||||||
|
assert not mock_conf.should_expose(state_config)
|
||||||
|
assert not mock_conf.should_expose(state_diagnostic)
|
||||||
|
|
||||||
entity_conf["should_expose"] = True
|
entity_conf["should_expose"] = True
|
||||||
assert mock_conf.should_expose(state)
|
assert mock_conf.should_expose(state)
|
||||||
|
# config and diagnostic entities should not be exposed
|
||||||
|
assert not mock_conf.should_expose(state_config)
|
||||||
|
assert not mock_conf.should_expose(state_diagnostic)
|
||||||
|
|
||||||
entity_conf["should_expose"] = None
|
entity_conf["should_expose"] = None
|
||||||
assert mock_conf.should_expose(state)
|
assert mock_conf.should_expose(state)
|
||||||
|
# config and diagnostic entities should not be exposed
|
||||||
|
assert not mock_conf.should_expose(state_config)
|
||||||
|
assert not mock_conf.should_expose(state_diagnostic)
|
||||||
|
|
||||||
await cloud_prefs.async_update(
|
await cloud_prefs.async_update(
|
||||||
google_default_expose=["sensor"],
|
google_default_expose=["sensor"],
|
||||||
|
|
Loading…
Reference in New Issue