From 80bc15e24b5c5665aa26ece36630dc6d6badc9de Mon Sep 17 00:00:00 2001
From: Bram Kragten <mail@bramkragten.nl>
Date: Fri, 27 Sep 2019 21:51:46 +0200
Subject: [PATCH] Update Alexa discovery description (#26933)

* Update Alexa discovery description

* Update description

* Fix test

* Filter special chars
---
 homeassistant/components/alexa/entities.py | 20 +++++++++++++-------
 tests/components/alexa/test_smart_home.py  | 18 +++++++++++++-----
 tests/components/cloud/test_client.py      |  2 +-
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py
index 03d153f5927..f0d72af23d5 100644
--- a/homeassistant/components/alexa/entities.py
+++ b/homeassistant/components/alexa/entities.py
@@ -52,6 +52,8 @@ from .capabilities import (
 
 ENTITY_ADAPTERS = Registry()
 
+TRANSLATION_TABLE = dict.fromkeys(map(ord, r"}{\/|\"()[]+~!><*%"), None)
+
 
 class DisplayCategory:
     """Possible display categories for Discovery response.
@@ -134,15 +136,18 @@ class AlexaEntity:
 
     def friendly_name(self):
         """Return the Alexa API friendly name."""
-        return self.entity_conf.get(CONF_NAME, self.entity.name)
+        return self.entity_conf.get(CONF_NAME, self.entity.name).translate(
+            TRANSLATION_TABLE
+        )
 
     def description(self):
         """Return the Alexa API description."""
-        return self.entity_conf.get(CONF_DESCRIPTION, self.entity.entity_id)
+        description = self.entity_conf.get(CONF_DESCRIPTION) or self.entity_id
+        return f"{description} via Home Assistant".translate(TRANSLATION_TABLE)
 
     def alexa_id(self):
         """Return the Alexa API entity id."""
-        return self.entity.entity_id.replace(".", "#")
+        return self.entity.entity_id.replace(".", "#").translate(TRANSLATION_TABLE)
 
     def display_categories(self):
         """Return a list of display categories."""
@@ -389,10 +394,11 @@ class SceneCapabilities(AlexaEntity):
     """Class to represent Scene capabilities."""
 
     def description(self):
-        """Return the description of the entity."""
-        # Required description as per Amazon Scene docs
-        scene_fmt = "{} (Scene connected via Home Assistant)"
-        return scene_fmt.format(AlexaEntity.description(self))
+        """Return the Alexa API description."""
+        description = AlexaEntity.description(self)
+        if "scene" not in description.casefold():
+            return f"{description} (Scene)"
+        return description
 
     def default_display_categories(self):
         """Return the display categories for this entity."""
diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py
index bd5a4d25edd..3cafa899024 100644
--- a/tests/components/alexa/test_smart_home.py
+++ b/tests/components/alexa/test_smart_home.py
@@ -1162,14 +1162,16 @@ async def test_entity_config(hass):
     request = get_new_request("Alexa.Discovery", "Discover")
 
     hass.states.async_set("light.test_1", "on", {"friendly_name": "Test light 1"})
+    hass.states.async_set("scene.test_1", "scening", {"friendly_name": "Test 1"})
 
     alexa_config = MockConfig(hass)
     alexa_config.entity_config = {
         "light.test_1": {
-            "name": "Config name",
+            "name": "Config *name*",
             "display_categories": "SWITCH",
-            "description": "Config description",
-        }
+            "description": "Config >!<description",
+        },
+        "scene.test_1": {"description": "Config description"},
     }
 
     msg = await smart_home.async_handle_message(hass, alexa_config, request)
@@ -1177,17 +1179,23 @@ async def test_entity_config(hass):
     assert "event" in msg
     msg = msg["event"]
 
-    assert len(msg["payload"]["endpoints"]) == 1
+    assert len(msg["payload"]["endpoints"]) == 2
 
     appliance = msg["payload"]["endpoints"][0]
     assert appliance["endpointId"] == "light#test_1"
     assert appliance["displayCategories"][0] == "SWITCH"
     assert appliance["friendlyName"] == "Config name"
-    assert appliance["description"] == "Config description"
+    assert appliance["description"] == "Config description via Home Assistant"
     assert_endpoint_capabilities(
         appliance, "Alexa.PowerController", "Alexa.EndpointHealth"
     )
 
+    scene = msg["payload"]["endpoints"][1]
+    assert scene["endpointId"] == "scene#test_1"
+    assert scene["displayCategories"][0] == "SCENE_TRIGGER"
+    assert scene["friendlyName"] == "Test 1"
+    assert scene["description"] == "Config description via Home Assistant (Scene)"
+
 
 async def test_logging_request(hass, events):
     """Test that we log requests."""
diff --git a/tests/components/cloud/test_client.py b/tests/components/cloud/test_client.py
index 5c895f09dbb..b7ac5f4cffd 100644
--- a/tests/components/cloud/test_client.py
+++ b/tests/components/cloud/test_client.py
@@ -53,7 +53,7 @@ async def test_handler_alexa(hass):
     assert len(endpoints) == 1
     device = endpoints[0]
 
-    assert device["description"] == "Config description"
+    assert device["description"] == "Config description via Home Assistant"
     assert device["friendlyName"] == "Config name"
     assert device["displayCategories"] == ["LIGHT"]
     assert device["manufacturerName"] == "Home Assistant"