diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index c47b05c264c..29b9c62ea1d 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -7,12 +7,14 @@ from typing import Any from hass_nabucasa import Cloud, cloud_api from hass_nabucasa.google_report_state import ErrorResponse +from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.google_assistant import DOMAIN as GOOGLE_DOMAIN from homeassistant.components.google_assistant.helpers import AbstractConfig from homeassistant.components.homeassistant.exposed_entities import ( async_listen_entity_updates, async_should_expose, ) +from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES from homeassistant.core import ( CoreState, @@ -22,6 +24,7 @@ from homeassistant.core import ( split_entity_id, ) from homeassistant.helpers import device_registry as dr, entity_registry as er, start +from homeassistant.helpers.entity import get_device_class from homeassistant.setup import async_setup_component from .const import ( @@ -39,6 +42,73 @@ _LOGGER = logging.getLogger(__name__) CLOUD_GOOGLE = f"{CLOUD_DOMAIN}.{GOOGLE_DOMAIN}" +SUPPORTED_DOMAINS = { + "alarm_control_panel", + "button", + "camera", + "climate", + "cover", + "fan", + "group", + "humidifier", + "input_boolean", + "input_button", + "input_select", + "light", + "lock", + "media_player", + "scene", + "script", + "select", + "switch", + "vacuum", +} + +SUPPORTED_BINARY_SENSOR_DEVICE_CLASSES = { + BinarySensorDeviceClass.DOOR, + BinarySensorDeviceClass.GARAGE_DOOR, + BinarySensorDeviceClass.LOCK, + BinarySensorDeviceClass.MOTION, + BinarySensorDeviceClass.OPENING, + BinarySensorDeviceClass.PRESENCE, + BinarySensorDeviceClass.WINDOW, +} + +SUPPORTED_SENSOR_DEVICE_CLASSES = { + SensorDeviceClass.AQI, + SensorDeviceClass.CO, + SensorDeviceClass.CO2, + SensorDeviceClass.HUMIDITY, + SensorDeviceClass.PM10, + SensorDeviceClass.PM25, + SensorDeviceClass.TEMPERATURE, + SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, +} + + +def _supported_legacy(hass: HomeAssistant, entity_id: str) -> bool: + """Return if the entity is supported. + + This is called when migrating from legacy config format to avoid exposing + all binary sensors and sensors. + """ + domain = split_entity_id(entity_id)[0] + if domain in SUPPORTED_DOMAINS: + return True + + device_class = get_device_class(hass, entity_id) + if ( + domain == "binary_sensor" + and device_class in SUPPORTED_BINARY_SENSOR_DEVICE_CLASSES + ): + return True + + if domain == "sensor" and device_class in SUPPORTED_SENSOR_DEVICE_CLASSES: + return True + + return False + + class CloudGoogleConfig(AbstractConfig): """HA Cloud Configuration for Google Assistant.""" @@ -180,9 +250,13 @@ class CloudGoogleConfig(AbstractConfig): # Backwards compat if default_expose is None: - return not auxiliary_entity + return not auxiliary_entity and _supported_legacy(self.hass, entity_id) - return not auxiliary_entity and split_entity_id(entity_id)[0] in default_expose + return ( + not auxiliary_entity + and split_entity_id(entity_id)[0] in default_expose + and _supported_legacy(self.hass, entity_id) + ) def _should_expose_entity_id(self, entity_id): """If an entity should be exposed.""" diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 738b3fa7cd7..8b927f7f3aa 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -611,3 +611,106 @@ async def test_google_config_migrate_expose_entity_prefs_default_none( entity_default = entity_registry.async_get(entity_default.entity_id) assert entity_default.options == {"cloud.google_assistant": {"should_expose": True}} + + +async def test_google_config_migrate_expose_entity_prefs_default( + hass: HomeAssistant, + cloud_prefs: CloudPreferences, + entity_registry: er.EntityRegistry, +) -> None: + """Test migrating Google entity config.""" + + assert await async_setup_component(hass, "homeassistant", {}) + + binary_sensor_supported = entity_registry.async_get_or_create( + "binary_sensor", + "test", + "binary_sensor_supported", + original_device_class="door", + suggested_object_id="supported", + ) + + binary_sensor_unsupported = entity_registry.async_get_or_create( + "binary_sensor", + "test", + "binary_sensor_unsupported", + original_device_class="battery", + suggested_object_id="unsupported", + ) + + light = entity_registry.async_get_or_create( + "light", + "test", + "unique", + suggested_object_id="light", + ) + + sensor_supported = entity_registry.async_get_or_create( + "sensor", + "test", + "sensor_supported", + original_device_class="temperature", + suggested_object_id="supported", + ) + + sensor_unsupported = entity_registry.async_get_or_create( + "sensor", + "test", + "sensor_unsupported", + original_device_class="battery", + suggested_object_id="unsupported", + ) + + water_heater = entity_registry.async_get_or_create( + "water_heater", + "test", + "unique", + suggested_object_id="water_heater", + ) + + await cloud_prefs.async_update( + google_enabled=True, + google_report_state=False, + google_settings_version=1, + ) + + cloud_prefs._prefs[PREF_GOOGLE_DEFAULT_EXPOSE] = [ + "binary_sensor", + "light", + "sensor", + "water_heater", + ] + conf = CloudGoogleConfig( + hass, GACTIONS_SCHEMA({}), "mock-user-id", cloud_prefs, Mock(is_logged_in=False) + ) + await conf.async_initialize() + + binary_sensor_supported = entity_registry.async_get( + binary_sensor_supported.entity_id + ) + assert binary_sensor_supported.options == { + "cloud.google_assistant": {"should_expose": True} + } + + binary_sensor_unsupported = entity_registry.async_get( + binary_sensor_unsupported.entity_id + ) + assert binary_sensor_unsupported.options == { + "cloud.google_assistant": {"should_expose": False} + } + + light = entity_registry.async_get(light.entity_id) + assert light.options == {"cloud.google_assistant": {"should_expose": True}} + + sensor_supported = entity_registry.async_get(sensor_supported.entity_id) + assert sensor_supported.options == { + "cloud.google_assistant": {"should_expose": True} + } + + sensor_unsupported = entity_registry.async_get(sensor_unsupported.entity_id) + assert sensor_unsupported.options == { + "cloud.google_assistant": {"should_expose": False} + } + + water_heater = entity_registry.async_get(water_heater.entity_id) + assert water_heater.options == {"cloud.google_assistant": {"should_expose": False}}