diff --git a/homeassistant/components/alexa/__init__.py b/homeassistant/components/alexa/__init__.py index e9bcccb3587..5861c4cc985 100644 --- a/homeassistant/components/alexa/__init__.py +++ b/homeassistant/components/alexa/__init__.py @@ -17,6 +17,8 @@ from .const import ( CONF_ENDPOINT, CONF_ENTITY_CONFIG, CONF_FILTER, + CONF_LOCALE, + CONF_SUPPORTED_LOCALES, CONF_TEXT, CONF_TITLE, CONF_UID, @@ -27,6 +29,7 @@ _LOGGER = logging.getLogger(__name__) CONF_FLASH_BRIEFINGS = "flash_briefings" CONF_SMART_HOME = "smart_home" +DEFAULT_LOCALE = "en-US" ALEXA_ENTITY_SCHEMA = vol.Schema( { @@ -41,6 +44,9 @@ SMART_HOME_SCHEMA = vol.Schema( vol.Optional(CONF_ENDPOINT): cv.string, vol.Optional(CONF_CLIENT_ID): cv.string, vol.Optional(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_LOCALE, default=DEFAULT_LOCALE): vol.In( + CONF_SUPPORTED_LOCALES + ), vol.Optional(CONF_FILTER, default={}): entityfilter.FILTER_SCHEMA, vol.Optional(CONF_ENTITY_CONFIG): {cv.entity_id: ALEXA_ENTITY_SCHEMA}, } diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index 3fdad2b3c92..26d07760747 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -56,6 +56,8 @@ class AlexaCapability: https://developer.amazon.com/docs/device-apis/message-guide.html """ + supported_locales = {"en-US"} + def __init__(self, entity, instance=None): """Initialize an Alexa capability.""" self.entity = entity @@ -239,6 +241,21 @@ class Alexa(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-interface.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa" @@ -250,6 +267,19 @@ class AlexaEndpointHealth(AlexaCapability): https://developer.amazon.com/docs/smarthome/state-reporting-for-a-smart-home-skill.html#report-state-when-alexa-requests-it """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -287,6 +317,19 @@ class AlexaPowerController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.PowerController" @@ -323,6 +366,17 @@ class AlexaLockController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-lockcontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-US", + "es-ES", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.LockController" @@ -357,6 +411,17 @@ class AlexaSceneController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-scenecontroller.html """ + supported_locales = { + "de-DE", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + } + def __init__(self, entity, supports_deactivation): """Initialize the entity.""" super().__init__(entity) @@ -373,6 +438,19 @@ class AlexaBrightnessController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-brightnesscontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.BrightnessController" @@ -404,6 +482,19 @@ class AlexaColorController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-colorcontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.ColorController" @@ -436,6 +527,19 @@ class AlexaColorTemperatureController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-colortemperaturecontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.ColorTemperatureController" @@ -465,6 +569,19 @@ class AlexaPercentageController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-percentagecontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.PercentageController" @@ -499,6 +616,8 @@ class AlexaSpeaker(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-speaker.html """ + supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.Speaker" @@ -510,6 +629,8 @@ class AlexaStepSpeaker(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-stepspeaker.html """ + supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.StepSpeaker" @@ -521,6 +642,8 @@ class AlexaPlaybackController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-playbackcontroller.html """ + supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US", "fr-FR"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.PlaybackController" @@ -554,6 +677,8 @@ class AlexaInputController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-inputcontroller.html """ + supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.InputController" @@ -582,6 +707,19 @@ class AlexaTemperatureSensor(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-temperaturesensor.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -637,6 +775,8 @@ class AlexaContactSensor(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-contactsensor.html """ + supported_locales = {"en-CA", "en-US"} + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -674,6 +814,8 @@ class AlexaMotionSensor(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-motionsensor.html """ + supported_locales = {"en-CA", "en-US"} + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -711,6 +853,19 @@ class AlexaThermostatController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-thermostatcontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -819,6 +974,19 @@ class AlexaPowerLevelController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-powerlevelcontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "fr-FR", + "it-IT", + "ja-JP", + } + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.PowerLevelController" @@ -854,6 +1022,8 @@ class AlexaSecurityPanelController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-securitypanelcontroller.html """ + supported_locales = {"en-AU", "en-CA", "en-IN", "en-US"} + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -906,6 +1076,21 @@ class AlexaModeController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-modecontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, entity, instance, non_controllable=False): """Initialize the entity.""" super().__init__(entity, instance) @@ -1031,6 +1216,21 @@ class AlexaRangeController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-rangecontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, entity, instance, non_controllable=False): """Initialize the entity.""" super().__init__(entity, instance) @@ -1198,6 +1398,21 @@ class AlexaToggleController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-togglecontroller.html """ + supported_locales = { + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "ja-JP", + } + def __init__(self, entity, instance, non_controllable=False): """Initialize the entity.""" super().__init__(entity, instance) @@ -1252,6 +1467,8 @@ class AlexaChannelController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-channelcontroller.html """ + supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.ChannelController" @@ -1263,6 +1480,8 @@ class AlexaDoorbellEventSource(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-doorbelleventsource.html """ + supported_locales = {"en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.DoorbellEventSource" @@ -1278,6 +1497,8 @@ class AlexaPlaybackStateReporter(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-playbackstatereporter.html """ + supported_locales = {"de-DE", "en-GB", "en-US", "fr-FR"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.PlaybackStateReporter" @@ -1314,6 +1535,8 @@ class AlexaSeekController(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-seekcontroller.html """ + supported_locales = {"de-DE", "en-GB", "en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.SeekController" @@ -1325,6 +1548,8 @@ class AlexaEventDetectionSensor(AlexaCapability): https://developer.amazon.com/docs/device-apis/alexa-eventdetectionsensor.html """ + supported_locales = {"en-US"} + def __init__(self, hass, entity): """Initialize the entity.""" super().__init__(entity) @@ -1382,6 +1607,8 @@ class AlexaEqualizerController(AlexaCapability): https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-equalizercontroller.html """ + supported_locales = {"en-US"} + def name(self): """Return the Alexa API name of this interface.""" return "Alexa.EqualizerController" diff --git a/homeassistant/components/alexa/config.py b/homeassistant/components/alexa/config.py index f98337d71c5..a6e45c61953 100644 --- a/homeassistant/components/alexa/config.py +++ b/homeassistant/components/alexa/config.py @@ -28,6 +28,11 @@ class AbstractConfig: """Endpoint for report state.""" return None + @property + def locale(self): + """Return config locale.""" + return None + @property def entity_config(self): """Return entity config.""" diff --git a/homeassistant/components/alexa/const.py b/homeassistant/components/alexa/const.py index 6968ab3a691..f5f19bbf955 100644 --- a/homeassistant/components/alexa/const.py +++ b/homeassistant/components/alexa/const.py @@ -19,6 +19,7 @@ CONF_ENTITY_CONFIG = "entity_config" CONF_ENDPOINT = "endpoint" CONF_CLIENT_ID = "client_id" CONF_CLIENT_SECRET = "client_secret" +CONF_LOCALE = "locale" ATTR_UID = "uid" ATTR_UPDATE_DATE = "updateDate" @@ -42,6 +43,20 @@ API_CHANGE = "change" CONF_DESCRIPTION = "description" CONF_DISPLAY_CATEGORIES = "display_categories" +CONF_SUPPORTED_LOCALES = ( + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "ja-JP", +) API_TEMP_UNITS = {TEMP_FAHRENHEIT: "FAHRENHEIT", TEMP_CELSIUS: "CELSIUS"} diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index 4321d289cec..d6fa0415640 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -260,16 +260,24 @@ class AlexaEntity: def serialize_discovery(self): """Serialize the entity for discovery.""" - return { + result = { "displayCategories": self.display_categories(), "cookie": {}, "endpointId": self.alexa_id(), "friendlyName": self.friendly_name(), "description": self.description(), "manufacturerName": "Home Assistant", - "capabilities": [i.serialize_discovery() for i in self.interfaces()], } + locale = self.config.locale + capabilities = [] + for i in self.interfaces(): + if locale in i.supported_locales: + capabilities.append(i.serialize_discovery()) + result["capabilities"] = capabilities + + return result + @callback def async_get_entities(hass, config) -> List[AlexaEntity]: diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index ce6c37a2b39..74c1b24d42b 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -412,6 +412,10 @@ async def async_api_lock(hass, config, directive, context): @HANDLERS.register(("Alexa.LockController", "Unlock")) async def async_api_unlock(hass, config, directive, context): """Process an unlock request.""" + if config.locale not in {"de-DE", "en-US", "ja-JP"}: + msg = f"The unlock directive is not supported for the following locales: {config.locale}" + raise AlexaInvalidDirectiveError(msg) + entity = directive.entity await hass.services.async_call( entity.domain, diff --git a/homeassistant/components/alexa/smart_home_http.py b/homeassistant/components/alexa/smart_home_http.py index 08d33ffa09c..7c745f8afdd 100644 --- a/homeassistant/components/alexa/smart_home_http.py +++ b/homeassistant/components/alexa/smart_home_http.py @@ -12,6 +12,7 @@ from .const import ( CONF_ENDPOINT, CONF_ENTITY_CONFIG, CONF_FILTER, + CONF_LOCALE, ) from .smart_home import async_handle_message from .state_report import async_enable_proactive_mode @@ -53,6 +54,11 @@ class AlexaConfig(AbstractConfig): """Return entity config.""" return self._config.get(CONF_ENTITY_CONFIG) or {} + @property + def locale(self): + """Return config locale.""" + return self._config.get(CONF_LOCALE) + def should_expose(self, entity_id): """If an entity should be exposed.""" return self._config[CONF_FILTER](entity_id) diff --git a/tests/components/alexa/__init__.py b/tests/components/alexa/__init__.py index 473a3c6e12b..fa2917edcad 100644 --- a/tests/components/alexa/__init__.py +++ b/tests/components/alexa/__init__.py @@ -8,6 +8,7 @@ from tests.common import async_mock_service TEST_URL = "https://api.amazonalexa.com/v3/events" TEST_TOKEN_URL = "https://api.amazon.com/auth/o2/token" +TEST_LOCALE = "en-US" class MockConfig(config.AbstractConfig): @@ -30,6 +31,11 @@ class MockConfig(config.AbstractConfig): """Endpoint for report state.""" return TEST_URL + @property + def locale(self): + """Return config locale.""" + return TEST_LOCALE + def should_expose(self, entity_id): """If an entity should be exposed.""" return True