diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py
index b5ffb1ef7e6..938101a7500 100644
--- a/homeassistant/components/alexa/capabilities.py
+++ b/homeassistant/components/alexa/capabilities.py
@@ -13,11 +13,9 @@ from homeassistant.const import (
     STATE_ALARM_ARMED_CUSTOM_BYPASS,
     STATE_ALARM_ARMED_HOME,
     STATE_ALARM_ARMED_NIGHT,
-    STATE_CLOSED,
     STATE_LOCKED,
     STATE_OFF,
     STATE_ON,
-    STATE_OPEN,
     STATE_PAUSED,
     STATE_PLAYING,
     STATE_UNAVAILABLE,
@@ -34,10 +32,16 @@ from .const import (
     DATE_FORMAT,
     PERCENTAGE_FAN_MAP,
     RANGE_FAN_MAP,
-    Catalog,
     Inputs,
 )
 from .errors import UnsupportedProperty
+from .resources import (
+    AlexaCapabilityResource,
+    AlexaGlobalCatalog,
+    AlexaModeResource,
+    AlexaPresetResource,
+    AlexaSemantics,
+)
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -108,12 +112,15 @@ class AlexaCapability:
 
     @staticmethod
     def capability_resources():
-        """Applicable to ToggleController, RangeController, and ModeController interfaces."""
+        """Return the capability object.
+
+        Applicable to ToggleController, RangeController, and ModeController interfaces.
+        """
         return []
 
     @staticmethod
     def configuration():
-        """Return the Configuration object."""
+        """Return the configuration object."""
         return []
 
     @staticmethod
@@ -121,6 +128,14 @@ class AlexaCapability:
         """Applicable only to media players."""
         return []
 
+    @staticmethod
+    def semantics():
+        """Return the semantics object.
+
+        Applicable to ToggleController, RangeController, and ModeController interfaces.
+        """
+        return []
+
     @staticmethod
     def supported_operations():
         """Return the supportedOperations object."""
@@ -130,6 +145,10 @@ class AlexaCapability:
         """Serialize according to the Discovery API."""
         result = {"type": "AlexaInterface", "interface": self.name(), "version": "3"}
 
+        instance = self.instance
+        if instance is not None:
+            result["instance"] = instance
+
         properties_supported = self.properties_supported()
         if properties_supported:
             result["properties"] = {
@@ -138,22 +157,19 @@ class AlexaCapability:
                 "retrievable": self.properties_retrievable(),
             }
 
-        # pylint: disable=assignment-from-none
         proactively_reported = self.capability_proactively_reported()
         if proactively_reported is not None:
             result["proactivelyReported"] = proactively_reported
 
-        # pylint: disable=assignment-from-none
         non_controllable = self.properties_non_controllable()
         if non_controllable is not None:
             result["properties"]["nonControllable"] = non_controllable
 
-        # pylint: disable=assignment-from-none
         supports_deactivation = self.supports_deactivation()
         if supports_deactivation is not None:
             result["supportsDeactivation"] = supports_deactivation
 
-        capability_resources = self.serialize_capability_resources()
+        capability_resources = self.capability_resources()
         if capability_resources:
             result["capabilityResources"] = capability_resources
 
@@ -161,10 +177,9 @@ class AlexaCapability:
         if configuration:
             result["configuration"] = configuration
 
-        # pylint: disable=assignment-from-none
-        instance = self.instance
-        if instance is not None:
-            result["instance"] = instance
+        semantics = self.semantics()
+        if semantics:
+            result["semantics"] = semantics
 
         supported_operations = self.supported_operations()
         if supported_operations:
@@ -196,36 +211,6 @@ class AlexaCapability:
 
                 yield result
 
-    def serialize_capability_resources(self):
-        """Return capabilityResources friendlyNames serialized for an API response."""
-        resources = self.capability_resources()
-        if resources:
-            return {"friendlyNames": self.serialize_friendly_names(resources)}
-
-        return None
-
-    @staticmethod
-    def serialize_friendly_names(resources):
-        """Return capabilityResources, ModeResources, or presetResources friendlyNames serialized for an API response."""
-        friendly_names = []
-        for resource in resources:
-            if resource["type"] == Catalog.LABEL_ASSET:
-                friendly_names.append(
-                    {
-                        "@type": Catalog.LABEL_ASSET,
-                        "value": {"assetId": resource["value"]},
-                    }
-                )
-            else:
-                friendly_names.append(
-                    {
-                        "@type": Catalog.LABEL_TEXT,
-                        "value": {"text": resource["value"], "locale": "en-US"},
-                    }
-                )
-
-        return friendly_names
-
 
 class Alexa(AlexaCapability):
     """Implements Alexa Interface.
@@ -906,6 +891,8 @@ class AlexaModeController(AlexaCapability):
     def __init__(self, entity, instance, non_controllable=False):
         """Initialize the entity."""
         super().__init__(entity, instance)
+        self._resource = None
+        self._semantics = None
         self.properties_non_controllable = lambda: non_controllable
 
     def name(self):
@@ -922,108 +909,102 @@ class AlexaModeController(AlexaCapability):
 
     def properties_retrievable(self):
         """Return True if properties can be retrieved."""
+        return True
 
     def get_property(self, name):
         """Read and return a property."""
         if name != "mode":
             raise UnsupportedProperty(name)
 
+        # Fan Direction
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
-            return self.entity.attributes.get(fan.ATTR_DIRECTION)
+            mode = self.entity.attributes.get(fan.ATTR_DIRECTION, None)
+            if mode in (fan.DIRECTION_FORWARD, fan.DIRECTION_REVERSE, STATE_UNKNOWN):
+                return f"{fan.ATTR_DIRECTION}.{mode}"
 
+        # Cover Position
         if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
-            return self.entity.attributes.get(cover.ATTR_POSITION)
+            # Return state instead of position when using ModeController.
+            mode = self.entity.state
+            if mode in (
+                cover.STATE_OPEN,
+                cover.STATE_OPENING,
+                cover.STATE_CLOSED,
+                cover.STATE_CLOSING,
+                STATE_UNKNOWN,
+            ):
+                return f"{cover.ATTR_POSITION}.{mode}"
 
         return None
 
     def configuration(self):
         """Return configuration with modeResources."""
-        return self.serialize_mode_resources()
+        if isinstance(self._resource, AlexaCapabilityResource):
+            return self._resource.serialize_configuration()
+
+        return None
 
     def capability_resources(self):
         """Return capabilityResources object."""
-        capability_resources = []
 
+        # Fan Direction Resource
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
-            capability_resources = [
-                {"type": Catalog.LABEL_ASSET, "value": Catalog.SETTING_DIRECTION}
-            ]
+            self._resource = AlexaModeResource(
+                [AlexaGlobalCatalog.SETTING_DIRECTION], False
+            )
+            self._resource.add_mode(
+                f"{fan.ATTR_DIRECTION}.{fan.DIRECTION_FORWARD}", [fan.DIRECTION_FORWARD]
+            )
+            self._resource.add_mode(
+                f"{fan.ATTR_DIRECTION}.{fan.DIRECTION_REVERSE}", [fan.DIRECTION_REVERSE]
+            )
+            return self._resource.serialize_capability_resources()
 
+        # Cover Position Resources
         if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
-            capability_resources = [
-                {"type": Catalog.LABEL_ASSET, "value": Catalog.SETTING_MODE},
-                {"type": Catalog.LABEL_ASSET, "value": Catalog.SETTING_PRESET},
-            ]
+            self._resource = AlexaModeResource(
+                ["Position", AlexaGlobalCatalog.SETTING_OPENING], False
+            )
+            self._resource.add_mode(
+                f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
+                [AlexaGlobalCatalog.VALUE_OPEN],
+            )
+            self._resource.add_mode(
+                f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
+                [AlexaGlobalCatalog.VALUE_CLOSE],
+            )
+            self._resource.add_mode(f"{cover.ATTR_POSITION}.custom", ["Custom"])
+            return self._resource.serialize_capability_resources()
 
-        return capability_resources
+        return None
 
-    def mode_resources(self):
-        """Return modeResources object."""
-        mode_resources = None
-        if self.instance == f"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
-            mode_resources = {
-                "ordered": False,
-                "resources": [
-                    {
-                        "value": f"{fan.ATTR_DIRECTION}.{fan.DIRECTION_FORWARD}",
-                        "friendly_names": [
-                            {"type": Catalog.LABEL_TEXT, "value": fan.DIRECTION_FORWARD}
-                        ],
-                    },
-                    {
-                        "value": f"{fan.ATTR_DIRECTION}.{fan.DIRECTION_REVERSE}",
-                        "friendly_names": [
-                            {"type": Catalog.LABEL_TEXT, "value": fan.DIRECTION_REVERSE}
-                        ],
-                    },
-                ],
-            }
+    def semantics(self):
+        """Build and return semantics object."""
 
+        # Cover Position
         if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
-            mode_resources = {
-                "ordered": False,
-                "resources": [
-                    {
-                        "value": f"{cover.ATTR_POSITION}.{STATE_OPEN}",
-                        "friendly_names": [
-                            {"type": Catalog.LABEL_TEXT, "value": "open"},
-                            {"type": Catalog.LABEL_TEXT, "value": "opened"},
-                            {"type": Catalog.LABEL_TEXT, "value": "raise"},
-                            {"type": Catalog.LABEL_TEXT, "value": "raised"},
-                        ],
-                    },
-                    {
-                        "value": f"{cover.ATTR_POSITION}.{STATE_CLOSED}",
-                        "friendly_names": [
-                            {"type": Catalog.LABEL_TEXT, "value": "close"},
-                            {"type": Catalog.LABEL_TEXT, "value": "closed"},
-                            {"type": Catalog.LABEL_TEXT, "value": "shut"},
-                            {"type": Catalog.LABEL_TEXT, "value": "lower"},
-                            {"type": Catalog.LABEL_TEXT, "value": "lowered"},
-                        ],
-                    },
-                ],
-            }
+            self._semantics = AlexaSemantics()
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_CLOSE, AlexaSemantics.ACTION_LOWER],
+                "SetMode",
+                {"mode": f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}"},
+            )
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_OPEN, AlexaSemantics.ACTION_RAISE],
+                "SetMode",
+                {"mode": f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}"},
+            )
+            self._semantics.add_states_to_value(
+                [AlexaSemantics.STATES_CLOSED],
+                f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
+            )
+            self._semantics.add_states_to_value(
+                [AlexaSemantics.STATES_OPEN],
+                f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
+            )
+            return self._semantics.serialize_semantics()
 
-        return mode_resources
-
-    def serialize_mode_resources(self):
-        """Return ModeResources, friendlyNames serialized for an API response."""
-        mode_resources = []
-        resources = self.mode_resources()
-        ordered = resources["ordered"]
-        for resource in resources["resources"]:
-            mode_value = resource["value"]
-            friendly_names = resource["friendly_names"]
-            result = {
-                "value": mode_value,
-                "modeResources": {
-                    "friendlyNames": self.serialize_friendly_names(friendly_names)
-                },
-            }
-            mode_resources.append(result)
-
-        return {"ordered": ordered, "supportedModes": mode_resources}
+        return None
 
 
 class AlexaRangeController(AlexaCapability):
@@ -1035,6 +1016,8 @@ class AlexaRangeController(AlexaCapability):
     def __init__(self, entity, instance, non_controllable=False):
         """Initialize the entity."""
         super().__init__(entity, instance)
+        self._resource = None
+        self._semantics = None
         self.properties_non_controllable = lambda: non_controllable
 
     def name(self):
@@ -1058,88 +1041,111 @@ class AlexaRangeController(AlexaCapability):
         if name != "rangeValue":
             raise UnsupportedProperty(name)
 
+        # Fan Speed
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
             speed = self.entity.attributes.get(fan.ATTR_SPEED)
             return RANGE_FAN_MAP.get(speed, 0)
 
+        # Cover Position
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+            return self.entity.attributes.get(cover.ATTR_CURRENT_POSITION)
+
+        # Cover Tilt Position
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}":
+            return self.entity.attributes.get(cover.ATTR_CURRENT_TILT_POSITION)
+
         return None
 
     def configuration(self):
         """Return configuration with presetResources."""
-        return self.serialize_preset_resources()
+        if isinstance(self._resource, AlexaCapabilityResource):
+            return self._resource.serialize_configuration()
+
+        return None
 
     def capability_resources(self):
         """Return capabilityResources object."""
-        capability_resources = []
 
+        # Fan Speed Resources
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
-            return [{"type": Catalog.LABEL_ASSET, "value": Catalog.SETTING_FANSPEED}]
-
-        return capability_resources
-
-    def preset_resources(self):
-        """Return presetResources object."""
-        preset_resources = []
-
-        if self.instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
-            preset_resources = {
-                "minimumValue": 1,
-                "maximumValue": 3,
-                "precision": 1,
-                "presets": [
-                    {
-                        "rangeValue": 1,
-                        "names": [
-                            {
-                                "type": Catalog.LABEL_ASSET,
-                                "value": Catalog.VALUE_MINIMUM,
-                            },
-                            {"type": Catalog.LABEL_ASSET, "value": Catalog.VALUE_LOW},
-                        ],
-                    },
-                    {
-                        "rangeValue": 2,
-                        "names": [
-                            {"type": Catalog.LABEL_ASSET, "value": Catalog.VALUE_MEDIUM}
-                        ],
-                    },
-                    {
-                        "rangeValue": 3,
-                        "names": [
-                            {
-                                "type": Catalog.LABEL_ASSET,
-                                "value": Catalog.VALUE_MAXIMUM,
-                            },
-                            {"type": Catalog.LABEL_ASSET, "value": Catalog.VALUE_HIGH},
-                        ],
-                    },
-                ],
-            }
-
-        return preset_resources
-
-    def serialize_preset_resources(self):
-        """Return PresetResources, friendlyNames serialized for an API response."""
-        preset_resources = []
-        resources = self.preset_resources()
-        for preset in resources["presets"]:
-            preset_resources.append(
-                {
-                    "rangeValue": preset["rangeValue"],
-                    "presetResources": {
-                        "friendlyNames": self.serialize_friendly_names(preset["names"])
-                    },
-                }
+            self._resource = AlexaPresetResource(
+                labels=[AlexaGlobalCatalog.SETTING_FAN_SPEED],
+                min_value=1,
+                max_value=3,
+                precision=1,
             )
+            self._resource.add_preset(
+                value=1,
+                labels=[AlexaGlobalCatalog.VALUE_LOW, AlexaGlobalCatalog.VALUE_MINIMUM],
+            )
+            self._resource.add_preset(value=2, labels=[AlexaGlobalCatalog.VALUE_MEDIUM])
+            self._resource.add_preset(
+                value=3,
+                labels=[
+                    AlexaGlobalCatalog.VALUE_HIGH,
+                    AlexaGlobalCatalog.VALUE_MAXIMUM,
+                ],
+            )
+            return self._resource.serialize_capability_resources()
 
-        return {
-            "supportedRange": {
-                "minimumValue": resources["minimumValue"],
-                "maximumValue": resources["maximumValue"],
-                "precision": resources["precision"],
-            },
-            "presets": preset_resources,
-        }
+        # Cover Position Resources
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+            self._resource = AlexaPresetResource(
+                ["Position", AlexaGlobalCatalog.SETTING_OPENING],
+                min_value=0,
+                max_value=100,
+                precision=1,
+                unit=AlexaGlobalCatalog.UNIT_PERCENT,
+            )
+            return self._resource.serialize_capability_resources()
+
+        # Cover Tilt Position Resources
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}":
+            self._resource = AlexaPresetResource(
+                ["Tilt Position", AlexaGlobalCatalog.SETTING_OPENING],
+                min_value=0,
+                max_value=100,
+                precision=1,
+                unit=AlexaGlobalCatalog.UNIT_PERCENT,
+            )
+            return self._resource.serialize_capability_resources()
+
+        return None
+
+    def semantics(self):
+        """Build and return semantics object."""
+
+        # Cover Position
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+            self._semantics = AlexaSemantics()
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_LOWER], "SetRangeValue", {"rangeValue": 0}
+            )
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_RAISE], "SetRangeValue", {"rangeValue": 100}
+            )
+            self._semantics.add_states_to_value([AlexaSemantics.STATES_CLOSED], value=0)
+            self._semantics.add_states_to_range(
+                [AlexaSemantics.STATES_OPEN], min_value=1, max_value=100
+            )
+            return self._semantics.serialize_semantics()
+
+        # Cover Tilt Position
+        if self.instance == f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}":
+            self._semantics = AlexaSemantics()
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_CLOSE], "SetRangeValue", {"rangeValue": 0}
+            )
+            self._semantics.add_action_to_directive(
+                [AlexaSemantics.ACTION_OPEN], "SetRangeValue", {"rangeValue": 100}
+            )
+            self._semantics.add_states_to_value([AlexaSemantics.STATES_CLOSED], value=0)
+            self._semantics.add_states_to_range(
+                [AlexaSemantics.STATES_OPEN], min_value=1, max_value=100
+            )
+            return self._semantics.serialize_semantics()
+
+        return None
 
 
 class AlexaToggleController(AlexaCapability):
@@ -1151,6 +1157,8 @@ class AlexaToggleController(AlexaCapability):
     def __init__(self, entity, instance, non_controllable=False):
         """Initialize the entity."""
         super().__init__(entity, instance)
+        self._resource = None
+        self._semantics = None
         self.properties_non_controllable = lambda: non_controllable
 
     def name(self):
@@ -1174,6 +1182,7 @@ class AlexaToggleController(AlexaCapability):
         if name != "toggleState":
             raise UnsupportedProperty(name)
 
+        # Fan Oscillating
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
             is_on = bool(self.entity.attributes.get(fan.ATTR_OSCILLATING))
             return "ON" if is_on else "OFF"
@@ -1182,16 +1191,15 @@ class AlexaToggleController(AlexaCapability):
 
     def capability_resources(self):
         """Return capabilityResources object."""
-        capability_resources = []
 
+        # Fan Oscillating Resource
         if self.instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
-            capability_resources = [
-                {"type": Catalog.LABEL_ASSET, "value": Catalog.SETTING_OSCILLATE},
-                {"type": Catalog.LABEL_TEXT, "value": "Rotate"},
-                {"type": Catalog.LABEL_TEXT, "value": "Rotation"},
-            ]
+            self._resource = AlexaCapabilityResource(
+                [AlexaGlobalCatalog.SETTING_OSCILLATE, "Rotate", "Rotation"]
+            )
+            return self._resource.serialize_capability_resources()
 
-        return capability_resources
+        return None
 
 
 class AlexaChannelController(AlexaCapability):
diff --git a/homeassistant/components/alexa/const.py b/homeassistant/components/alexa/const.py
index 2c62e1a485a..f1a86859da9 100644
--- a/homeassistant/components/alexa/const.py
+++ b/homeassistant/components/alexa/const.py
@@ -117,163 +117,6 @@ class Cause:
     VOICE_INTERACTION = "VOICE_INTERACTION"
 
 
-class Catalog:
-    """The Global Alexa catalog.
-
-    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#global-alexa-catalog
-
-    You can use the global Alexa catalog for pre-defined names of devices, settings, values, and units.
-    This catalog is localized into all the languages that Alexa supports.
-
-    You can reference the following catalog of pre-defined friendly names.
-    Each item in the following list is an asset identifier followed by its supported friendly names.
-    The first friendly name for each identifier is the one displayed in the Alexa mobile app.
-    """
-
-    LABEL_ASSET = "asset"
-    LABEL_TEXT = "text"
-
-    # Shower
-    DEVICENAME_SHOWER = "Alexa.DeviceName.Shower"
-
-    # Washer, Washing Machine
-    DEVICENAME_WASHER = "Alexa.DeviceName.Washer"
-
-    # Router, Internet Router, Network Router, Wifi Router, Net Router
-    DEVICENAME_ROUTER = "Alexa.DeviceName.Router"
-
-    # Fan, Blower
-    DEVICENAME_FAN = "Alexa.DeviceName.Fan"
-
-    # Air Purifier, Air Cleaner,Clean Air Machine
-    DEVICENAME_AIRPURIFIER = "Alexa.DeviceName.AirPurifier"
-
-    # Space Heater, Portable Heater
-    DEVICENAME_SPACEHEATER = "Alexa.DeviceName.SpaceHeater"
-
-    # Rain Head, Overhead shower, Rain Shower, Rain Spout, Rain Faucet
-    SHOWER_RAINHEAD = "Alexa.Shower.RainHead"
-
-    # Handheld Shower, Shower Wand, Hand Shower
-    SHOWER_HANDHELD = "Alexa.Shower.HandHeld"
-
-    # Water Temperature, Water Temp, Water Heat
-    SETTING_WATERTEMPERATURE = "Alexa.Setting.WaterTemperature"
-
-    # Temperature, Temp
-    SETTING_TEMPERATURE = "Alexa.Setting.Temperature"
-
-    # Wash Cycle, Wash Preset, Wash setting
-    SETTING_WASHCYCLE = "Alexa.Setting.WashCycle"
-
-    # 2.4G Guest Wi-Fi, 2.4G Guest Network, Guest Network 2.4G, 2G Guest Wifi
-    SETTING_2GGUESTWIFI = "Alexa.Setting.2GGuestWiFi"
-
-    # 5G Guest Wi-Fi, 5G Guest Network, Guest Network 5G, 5G Guest Wifi
-    SETTING_5GGUESTWIFI = "Alexa.Setting.5GGuestWiFi"
-
-    # Guest Wi-fi, Guest Network, Guest Net
-    SETTING_GUESTWIFI = "Alexa.Setting.GuestWiFi"
-
-    # Auto, Automatic, Automatic Mode, Auto Mode
-    SETTING_AUTO = "Alexa.Setting.Auto"
-
-    #     #Night, Night Mode
-    SETTING_NIGHT = "Alexa.Setting.Night"
-
-    # Quiet, Quiet Mode, Noiseless, Silent
-    SETTING_QUIET = "Alexa.Setting.Quiet"
-
-    # Oscillate, Swivel, Oscillation, Spin, Back and forth
-    SETTING_OSCILLATE = "Alexa.Setting.Oscillate"
-
-    # Fan Speed, Airflow speed, Wind Speed, Air speed, Air velocity
-    SETTING_FANSPEED = "Alexa.Setting.FanSpeed"
-
-    # Preset, Setting
-    SETTING_PRESET = "Alexa.Setting.Preset"
-
-    # Mode
-    SETTING_MODE = "Alexa.Setting.Mode"
-
-    # Direction
-    SETTING_DIRECTION = "Alexa.Setting.Direction"
-
-    # Delicates, Delicate
-    VALUE_DELICATE = "Alexa.Value.Delicate"
-
-    # Quick Wash, Fast Wash, Wash Quickly, Speed Wash
-    VALUE_QUICKWASH = "Alexa.Value.QuickWash"
-
-    # Maximum, Max
-    VALUE_MAXIMUM = "Alexa.Value.Maximum"
-
-    # Minimum, Min
-    VALUE_MINIMUM = "Alexa.Value.Minimum"
-
-    # High
-    VALUE_HIGH = "Alexa.Value.High"
-
-    # Low
-    VALUE_LOW = "Alexa.Value.Low"
-
-    # Medium, Mid
-    VALUE_MEDIUM = "Alexa.Value.Medium"
-
-
-class Unit:
-    """Alexa Units of Measure.
-
-    https://developer.amazon.com/docs/device-apis/alexa-property-schemas.html#units-of-measure
-    """
-
-    ANGLE_DEGREES = "Alexa.Unit.Angle.Degrees"
-
-    ANGLE_RADIANS = "Alexa.Unit.Angle.Radians"
-
-    DISTANCE_FEET = "Alexa.Unit.Distance.Feet"
-
-    DISTANCE_INCHES = "Alexa.Unit.Distance.Inches"
-
-    DISTANCE_KILOMETERS = "Alexa.Unit.Distance.Kilometers"
-
-    DISTANCE_METERS = "Alexa.Unit.Distance.Meters"
-
-    DISTANCE_MILES = "Alexa.Unit.Distance.Miles"
-
-    DISTANCE_YARDS = "Alexa.Unit.Distance.Yards"
-
-    MASS_GRAMS = "Alexa.Unit.Mass.Grams"
-
-    MASS_KILOGRAMS = "Alexa.Unit.Mass.Kilograms"
-
-    PERCENT = "Alexa.Unit.Percent"
-
-    TEMPERATURE_CELSIUS = "Alexa.Unit.Temperature.Celsius"
-
-    TEMPERATURE_DEGREES = "Alexa.Unit.Temperature.Degrees"
-
-    TEMPERATURE_FAHRENHEIT = "Alexa.Unit.Temperature.Fahrenheit"
-
-    TEMPERATURE_KELVIN = "Alexa.Unit.Temperature.Kelvin"
-
-    VOLUME_CUBICFEET = "Alexa.Unit.Volume.CubicFeet"
-
-    VOLUME_CUBICMETERS = "Alexa.Unit.Volume.CubicMeters"
-
-    VOLUME_GALLONS = "Alexa.Unit.Volume.Gallons"
-
-    VOLUME_LITERS = "Alexa.Unit.Volume.Liters"
-
-    VOLUME_PINTS = "Alexa.Unit.Volume.Pints"
-
-    VOLUME_QUARTS = "Alexa.Unit.Volume.Quarts"
-
-    WEIGHT_OUNCES = "Alexa.Unit.Weight.Ounces"
-
-    WEIGHT_POUNDS = "Alexa.Unit.Weight.Pounds"
-
-
 class Inputs:
     """Valid names for the InputController.
 
diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py
index 017686df607..2a3355434a3 100644
--- a/homeassistant/components/alexa/entities.py
+++ b/homeassistant/components/alexa/entities.py
@@ -83,6 +83,9 @@ class DisplayCategory:
     # Indicates media devices with video or photo capabilities.
     CAMERA = "CAMERA"
 
+    # Indicates a non-mobile computer, such as a desktop computer.
+    COMPUTER = "COMPUTER"
+
     # Indicates an endpoint that detects and reports contact.
     CONTACT_SENSOR = "CONTACT_SENSOR"
 
@@ -92,27 +95,60 @@ class DisplayCategory:
     # Indicates a doorbell.
     DOORBELL = "DOORBELL"
 
+    # Indicates a window covering on the outside of a structure.
+    EXTERIOR_BLIND = "EXTERIOR_BLIND"
+
     # Indicates a fan.
     FAN = "FAN"
 
+    # Indicates a game console, such as Microsoft Xbox or Nintendo Switch
+    GAME_CONSOLE = "GAME_CONSOLE"
+
+    # Indicates a garage door. Garage doors must implement the ModeController interface to open and close the door.
+    GARAGE_DOOR = "GARAGE_DOOR"
+
+    # Indicates a window covering on the inside of a structure.
+    INTERIOR_BLIND = "INTERIOR_BLIND"
+
+    # Indicates a laptop or other mobile computer.
+    LAPTOP = "LAPTOP"
+
     # Indicates light sources or fixtures.
     LIGHT = "LIGHT"
 
     # Indicates a microwave oven.
     MICROWAVE = "MICROWAVE"
 
+    # Indicates a mobile phone.
+    MOBILE_PHONE = "MOBILE_PHONE"
+
     # Indicates an endpoint that detects and reports motion.
     MOTION_SENSOR = "MOTION_SENSOR"
 
+    # Indicates a network-connected music system.
+    MUSIC_SYSTEM = "MUSIC_SYSTEM"
+
     # An endpoint that cannot be described in on of the other categories.
     OTHER = "OTHER"
 
+    # Indicates a network router.
+    NETWORK_HARDWARE = "NETWORK_HARDWARE"
+
+    # Indicates an oven cooking appliance.
+    OVEN = "OVEN"
+
+    # Indicates a non-mobile phone, such as landline or an IP phone.
+    PHONE = "PHONE"
+
     # Describes a combination of devices set to a specific state, when the
     # order of the state change is not important. For example a bedtime scene
     # might include turning off lights and lowering the thermostat, but the
     # order is unimportant.    Applies to Scenes
     SCENE_TRIGGER = "SCENE_TRIGGER"
 
+    # Indicates a projector screen.
+    SCREEN = "SCREEN"
+
     # Indicates a security panel.
     SECURITY_PANEL = "SECURITY_PANEL"
 
@@ -126,10 +162,16 @@ class DisplayCategory:
     # Indicates the endpoint is a speaker or speaker system.
     SPEAKER = "SPEAKER"
 
+    # Indicates a streaming device such as Apple TV, Chromecast, or Roku.
+    STREAMING_DEVICE = "STREAMING_DEVICE"
+
     # Indicates in-wall switches wired to the electrical system.  Can control a
     # variety of devices.
     SWITCH = "SWITCH"
 
+    # Indicates a tablet computer.
+    TABLET = "TABLET"
+
     # Indicates endpoints that report the temperature only.
     TEMPERATURE_SENSOR = "TEMPERATURE_SENSOR"
 
@@ -140,6 +182,9 @@ class DisplayCategory:
     # Indicates the endpoint is a television.
     TV = "TV"
 
+    # Indicates a network-connected wearable device, such as an Apple Watch, Fitbit, or Samsung Gear.
+    WEARABLE = "WEARABLE"
+
 
 class AlexaEntity:
     """An adaptation of an entity, expressed in Alexa's terms.
@@ -318,20 +363,40 @@ class CoverCapabilities(AlexaEntity):
     def default_display_categories(self):
         """Return the display categories for this entity."""
         device_class = self.entity.attributes.get(ATTR_DEVICE_CLASS)
-        if device_class in (cover.DEVICE_CLASS_GARAGE, cover.DEVICE_CLASS_DOOR):
+        if device_class == cover.DEVICE_CLASS_GARAGE:
+            return [DisplayCategory.GARAGE_DOOR]
+        if device_class == cover.DEVICE_CLASS_DOOR:
             return [DisplayCategory.DOOR]
+        if device_class in (
+            cover.DEVICE_CLASS_BLIND,
+            cover.DEVICE_CLASS_SHADE,
+            cover.DEVICE_CLASS_CURTAIN,
+        ):
+            return [DisplayCategory.INTERIOR_BLIND]
+        if device_class in (
+            cover.DEVICE_CLASS_WINDOW,
+            cover.DEVICE_CLASS_AWNING,
+            cover.DEVICE_CLASS_SHUTTER,
+        ):
+            return [DisplayCategory.EXTERIOR_BLIND]
+
         return [DisplayCategory.OTHER]
 
     def interfaces(self):
         """Yield the supported interfaces."""
-        yield AlexaPowerController(self.entity)
         supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
         if supported & cover.SUPPORT_SET_POSITION:
-            yield AlexaPercentageController(self.entity)
-        if supported & (cover.SUPPORT_CLOSE | cover.SUPPORT_OPEN):
+            yield AlexaRangeController(
+                self.entity, instance=f"{cover.DOMAIN}.{cover.ATTR_POSITION}"
+            )
+        elif supported & (cover.SUPPORT_CLOSE | cover.SUPPORT_OPEN):
             yield AlexaModeController(
                 self.entity, instance=f"{cover.DOMAIN}.{cover.ATTR_POSITION}"
             )
+        if supported & cover.SUPPORT_SET_TILT_POSITION:
+            yield AlexaRangeController(
+                self.entity, instance=f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}"
+            )
         yield AlexaEndpointHealth(self.hass, self.entity)
         yield Alexa(self.hass)
 
@@ -355,6 +420,7 @@ class LightCapabilities(AlexaEntity):
             yield AlexaColorController(self.entity)
         if supported & light.SUPPORT_COLOR_TEMP:
             yield AlexaColorTemperatureController(self.entity)
+
         yield AlexaEndpointHealth(self.hass, self.entity)
         yield Alexa(self.hass)
 
@@ -370,6 +436,7 @@ class FanCapabilities(AlexaEntity):
     def interfaces(self):
         """Yield the supported interfaces."""
         yield AlexaPowerController(self.entity)
+
         supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
         if supported & fan.SUPPORT_SET_SPEED:
             yield AlexaPercentageController(self.entity)
@@ -377,7 +444,6 @@ class FanCapabilities(AlexaEntity):
             yield AlexaRangeController(
                 self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_SPEED}"
             )
-
         if supported & fan.SUPPORT_OSCILLATE:
             yield AlexaToggleController(
                 self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}"
diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py
index efb4f59514d..b5603af7402 100644
--- a/homeassistant/components/alexa/handlers.py
+++ b/homeassistant/components/alexa/handlers.py
@@ -20,6 +20,7 @@ from homeassistant.const import (
     SERVICE_MEDIA_PREVIOUS_TRACK,
     SERVICE_MEDIA_STOP,
     SERVICE_SET_COVER_POSITION,
+    SERVICE_SET_COVER_TILT_POSITION,
     SERVICE_TURN_OFF,
     SERVICE_TURN_ON,
     SERVICE_UNLOCK,
@@ -28,8 +29,6 @@ from homeassistant.const import (
     SERVICE_VOLUME_SET,
     SERVICE_VOLUME_UP,
     STATE_ALARM_DISARMED,
-    STATE_CLOSED,
-    STATE_OPEN,
     TEMP_CELSIUS,
     TEMP_FAHRENHEIT,
 )
@@ -113,9 +112,7 @@ async def async_api_turn_on(hass, config, directive, context):
         domain = ha.DOMAIN
 
     service = SERVICE_TURN_ON
-    if domain == cover.DOMAIN:
-        service = cover.SERVICE_OPEN_COVER
-    elif domain == media_player.DOMAIN:
+    if domain == media_player.DOMAIN:
         supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
         power_features = media_player.SUPPORT_TURN_ON | media_player.SUPPORT_TURN_OFF
         if not supported & power_features:
@@ -141,9 +138,7 @@ async def async_api_turn_off(hass, config, directive, context):
         domain = ha.DOMAIN
 
     service = SERVICE_TURN_OFF
-    if entity.domain == cover.DOMAIN:
-        service = cover.SERVICE_CLOSE_COVER
-    elif domain == media_player.DOMAIN:
+    if domain == media_player.DOMAIN:
         supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
         power_features = media_player.SUPPORT_TURN_ON | media_player.SUPPORT_TURN_OFF
         if not supported & power_features:
@@ -348,10 +343,6 @@ async def async_api_set_percentage(hass, config, directive, context):
             speed = "high"
         data[fan.ATTR_SPEED] = speed
 
-    elif entity.domain == cover.DOMAIN:
-        service = SERVICE_SET_COVER_POSITION
-        data[cover.ATTR_POSITION] = percentage
-
     await hass.services.async_call(
         entity.domain, service, data, blocking=False, context=context
     )
@@ -385,13 +376,6 @@ async def async_api_adjust_percentage(hass, config, directive, context):
 
         data[fan.ATTR_SPEED] = speed
 
-    elif entity.domain == cover.DOMAIN:
-        service = SERVICE_SET_COVER_POSITION
-
-        current = entity.attributes.get(cover.ATTR_POSITION)
-
-        data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
-
     await hass.services.async_call(
         entity.domain, service, data, blocking=False, context=context
     )
@@ -960,32 +944,35 @@ async def async_api_disarm(hass, config, directive, context):
 
 @HANDLERS.register(("Alexa.ModeController", "SetMode"))
 async def async_api_set_mode(hass, config, directive, context):
-    """Process a next request."""
+    """Process a SetMode directive."""
     entity = directive.entity
     instance = directive.instance
     domain = entity.domain
     service = None
     data = {ATTR_ENTITY_ID: entity.entity_id}
-    capability_mode = directive.payload["mode"]
-
-    if domain not in (fan.DOMAIN, cover.DOMAIN):
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
+    mode = directive.payload["mode"]
 
+    # Fan Direction
     if instance == f"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
-        _, direction = capability_mode.split(".")
+        _, direction = mode.split(".")
         if direction in (fan.DIRECTION_REVERSE, fan.DIRECTION_FORWARD):
             service = fan.SERVICE_SET_DIRECTION
             data[fan.ATTR_DIRECTION] = direction
 
-    if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
-        _, position = capability_mode.split(".")
+    # Cover Position
+    elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+        _, position = mode.split(".")
 
-        if position == STATE_CLOSED:
+        if position == cover.STATE_CLOSED:
             service = cover.SERVICE_CLOSE_COVER
-
-        if position == STATE_OPEN:
+        elif position == cover.STATE_OPEN:
             service = cover.SERVICE_OPEN_COVER
+        elif position == "custom":
+            service = cover.SERVICE_STOP_COVER
+
+    else:
+        msg = "Entity does not support directive"
+        raise AlexaInvalidDirectiveError(msg)
 
     await hass.services.async_call(
         domain, service, data, blocking=False, context=context
@@ -997,7 +984,7 @@ async def async_api_set_mode(hass, config, directive, context):
             "namespace": "Alexa.ModeController",
             "instance": instance,
             "name": "mode",
-            "value": capability_mode,
+            "value": mode,
         }
     )
 
@@ -1008,24 +995,13 @@ async def async_api_set_mode(hass, config, directive, context):
 async def async_api_adjust_mode(hass, config, directive, context):
     """Process a AdjustMode request.
 
-    Requires modeResources to be ordered.
-    Only modes that are ordered support the adjustMode directive.
+    Requires capabilityResources supportedModes to be ordered.
+    Only supportedModes with ordered=True support the adjustMode directive.
     """
-    entity = directive.entity
-    instance = directive.instance
-    domain = entity.domain
 
-    if domain != fan.DOMAIN:
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
-
-    if instance is None:
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
-
-    # No modeResources are currently ordered to support this request.
-
-    return directive.response()
+    # Currently no supportedModes are configured with ordered=True to support this request.
+    msg = "Entity does not support directive"
+    raise AlexaInvalidDirectiveError(msg)
 
 
 @HANDLERS.register(("Alexa.ToggleController", "TurnOn"))
@@ -1037,19 +1013,29 @@ async def async_api_toggle_on(hass, config, directive, context):
     service = None
     data = {ATTR_ENTITY_ID: entity.entity_id}
 
-    if domain != fan.DOMAIN:
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
-
+    # Fan Oscillating
     if instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
         service = fan.SERVICE_OSCILLATE
         data[fan.ATTR_OSCILLATING] = True
+    else:
+        msg = "Entity does not support directive"
+        raise AlexaInvalidDirectiveError(msg)
 
     await hass.services.async_call(
         domain, service, data, blocking=False, context=context
     )
 
-    return directive.response()
+    response = directive.response()
+    response.add_context_property(
+        {
+            "namespace": "Alexa.ToggleController",
+            "instance": instance,
+            "name": "toggleState",
+            "value": "ON",
+        }
+    )
+
+    return response
 
 
 @HANDLERS.register(("Alexa.ToggleController", "TurnOff"))
@@ -1061,19 +1047,29 @@ async def async_api_toggle_off(hass, config, directive, context):
     service = None
     data = {ATTR_ENTITY_ID: entity.entity_id}
 
-    if domain != fan.DOMAIN:
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
-
+    # Fan Oscillating
     if instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
         service = fan.SERVICE_OSCILLATE
         data[fan.ATTR_OSCILLATING] = False
+    else:
+        msg = "Entity does not support directive"
+        raise AlexaInvalidDirectiveError(msg)
 
     await hass.services.async_call(
         domain, service, data, blocking=False, context=context
     )
 
-    return directive.response()
+    response = directive.response()
+    response.add_context_property(
+        {
+            "namespace": "Alexa.ToggleController",
+            "instance": instance,
+            "name": "toggleState",
+            "value": "OFF",
+        }
+    )
+
+    return response
 
 
 @HANDLERS.register(("Alexa.RangeController", "SetRangeValue"))
@@ -1086,10 +1082,7 @@ async def async_api_set_range(hass, config, directive, context):
     data = {ATTR_ENTITY_ID: entity.entity_id}
     range_value = int(directive.payload["rangeValue"])
 
-    if domain != fan.DOMAIN:
-        msg = "Entity does not support directive"
-        raise AlexaInvalidDirectiveError(msg)
-
+    # Fan Speed
     if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
         service = fan.SERVICE_SET_SPEED
         speed = SPEED_FAN_MAP.get(range_value, None)
@@ -1103,11 +1096,45 @@ async def async_api_set_range(hass, config, directive, context):
 
         data[fan.ATTR_SPEED] = speed
 
+    # Cover Position
+    elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+        if range_value == 0:
+            service = cover.SERVICE_CLOSE_COVER
+        elif range_value == 100:
+            service = cover.SERVICE_OPEN_COVER
+        else:
+            service = cover.SERVICE_SET_COVER_POSITION
+            data[cover.ATTR_POSITION] = range_value
+
+    # Cover Tilt Position
+    elif instance == f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}":
+        if range_value == 0:
+            service = cover.SERVICE_CLOSE_COVER_TILT
+        elif range_value == 100:
+            service = cover.SERVICE_OPEN_COVER_TILT
+        else:
+            service = cover.SERVICE_SET_COVER_TILT_POSITION
+            data[cover.ATTR_POSITION] = range_value
+
+    else:
+        msg = "Entity does not support directive"
+        raise AlexaInvalidDirectiveError(msg)
+
     await hass.services.async_call(
         domain, service, data, blocking=False, context=context
     )
 
-    return directive.response()
+    response = directive.response()
+    response.add_context_property(
+        {
+            "namespace": "Alexa.RangeController",
+            "instance": instance,
+            "name": "rangeValue",
+            "value": range_value,
+        }
+    )
+
+    return response
 
 
 @HANDLERS.register(("Alexa.RangeController", "AdjustRangeValue"))
@@ -1119,24 +1146,56 @@ async def async_api_adjust_range(hass, config, directive, context):
     service = None
     data = {ATTR_ENTITY_ID: entity.entity_id}
     range_delta = int(directive.payload["rangeValueDelta"])
+    response_value = 0
 
+    # Fan Speed
     if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
         service = fan.SERVICE_SET_SPEED
-
-        # adjust range
         current_range = RANGE_FAN_MAP.get(entity.attributes.get(fan.ATTR_SPEED), 0)
-        speed = SPEED_FAN_MAP.get(max(0, range_delta + current_range), fan.SPEED_OFF)
+        speed = SPEED_FAN_MAP.get(
+            min(3, max(0, range_delta + current_range)), fan.SPEED_OFF
+        )
 
         if speed == fan.SPEED_OFF:
             service = fan.SERVICE_TURN_OFF
 
-        data[fan.ATTR_SPEED] = speed
+        data[fan.ATTR_SPEED] = response_value = speed
+
+    # Cover Position
+    elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
+        service = SERVICE_SET_COVER_POSITION
+        current = entity.attributes.get(cover.ATTR_POSITION)
+        data[cover.ATTR_POSITION] = response_value = min(
+            100, max(0, range_delta + current)
+        )
+
+    # Cover Tilt Position
+    elif instance == f"{cover.DOMAIN}.{cover.ATTR_TILT_POSITION}":
+        service = SERVICE_SET_COVER_TILT_POSITION
+        current = entity.attributes.get(cover.ATTR_TILT_POSITION)
+        data[cover.ATTR_TILT_POSITION] = response_value = min(
+            100, max(0, range_delta + current)
+        )
+
+    else:
+        msg = "Entity does not support directive"
+        raise AlexaInvalidDirectiveError(msg)
 
     await hass.services.async_call(
         domain, service, data, blocking=False, context=context
     )
 
-    return directive.response()
+    response = directive.response()
+    response.add_context_property(
+        {
+            "namespace": "Alexa.RangeController",
+            "instance": instance,
+            "name": "rangeValue",
+            "value": response_value,
+        }
+    )
+
+    return response
 
 
 @HANDLERS.register(("Alexa.ChannelController", "ChangeChannel"))
diff --git a/homeassistant/components/alexa/resources.py b/homeassistant/components/alexa/resources.py
new file mode 100644
index 00000000000..061005252dc
--- /dev/null
+++ b/homeassistant/components/alexa/resources.py
@@ -0,0 +1,387 @@
+"""Alexa Resources and Assets."""
+
+
+class AlexaGlobalCatalog:
+    """The Global Alexa catalog.
+
+    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#global-alexa-catalog
+
+    You can use the global Alexa catalog for pre-defined names of devices, settings, values, and units.
+    This catalog is localized into all the languages that Alexa supports.
+
+    You can reference the following catalog of pre-defined friendly names.
+    Each item in the following list is an asset identifier followed by its supported friendly names.
+    The first friendly name for each identifier is the one displayed in the Alexa mobile app.
+    """
+
+    # Air Purifier, Air Cleaner,Clean Air Machine
+    DEVICE_NAME_AIR_PURIFIER = "Alexa.DeviceName.AirPurifier"
+
+    # Fan, Blower
+    DEVICE_NAME_FAN = "Alexa.DeviceName.Fan"
+
+    # Router, Internet Router, Network Router, Wifi Router, Net Router
+    DEVICE_NAME_ROUTER = "Alexa.DeviceName.Router"
+
+    # Shade, Blind, Curtain, Roller, Shutter, Drape, Awning, Window shade, Interior blind
+    DEVICE_NAME_SHADE = "Alexa.DeviceName.Shade"
+
+    # Shower
+    DEVICE_NAME_SHOWER = "Alexa.DeviceName.Shower"
+
+    # Space Heater, Portable Heater
+    DEVICE_NAME_SPACE_HEATER = "Alexa.DeviceName.SpaceHeater"
+
+    # Washer, Washing Machine
+    DEVICE_NAME_WASHER = "Alexa.DeviceName.Washer"
+
+    # 2.4G Guest Wi-Fi, 2.4G Guest Network, Guest Network 2.4G, 2G Guest Wifi
+    SETTING_2G_GUEST_WIFI = "Alexa.Setting.2GGuestWiFi"
+
+    # 5G Guest Wi-Fi, 5G Guest Network, Guest Network 5G, 5G Guest Wifi
+    SETTING_5G_GUEST_WIFI = "Alexa.Setting.5GGuestWiFi"
+
+    # Auto, Automatic, Automatic Mode, Auto Mode
+    SETTING_AUTO = "Alexa.Setting.Auto"
+
+    # Direction
+    SETTING_DIRECTION = "Alexa.Setting.Direction"
+
+    # Dry Cycle, Dry Preset, Dry Setting, Dryer Cycle, Dryer Preset, Dryer Setting
+    SETTING_DRY_CYCLE = "Alexa.Setting.DryCycle"
+
+    # Fan Speed, Airflow speed, Wind Speed, Air speed, Air velocity
+    SETTING_FAN_SPEED = "Alexa.Setting.FanSpeed"
+
+    # Guest Wi-fi, Guest Network, Guest Net
+    SETTING_GUEST_WIFI = "Alexa.Setting.GuestWiFi"
+
+    # Heat
+    SETTING_HEAT = "Alexa.Setting.Heat"
+
+    # Mode
+    SETTING_MODE = "Alexa.Setting.Mode"
+
+    # Night, Night Mode
+    SETTING_NIGHT = "Alexa.Setting.Night"
+
+    # Opening, Height, Lift, Width
+    SETTING_OPENING = "Alexa.Setting.Opening"
+
+    # Oscillate, Swivel, Oscillation, Spin, Back and forth
+    SETTING_OSCILLATE = "Alexa.Setting.Oscillate"
+
+    # Preset, Setting
+    SETTING_PRESET = "Alexa.Setting.Preset"
+
+    # Quiet, Quiet Mode, Noiseless, Silent
+    SETTING_QUIET = "Alexa.Setting.Quiet"
+
+    # Temperature, Temp
+    SETTING_TEMPERATURE = "Alexa.Setting.Temperature"
+
+    # Wash Cycle, Wash Preset, Wash setting
+    SETTING_WASH_CYCLE = "Alexa.Setting.WashCycle"
+
+    # Water Temperature, Water Temp, Water Heat
+    SETTING_WATER_TEMPERATURE = "Alexa.Setting.WaterTemperature"
+
+    # Handheld Shower, Shower Wand, Hand Shower
+    SHOWER_HAND_HELD = "Alexa.Shower.HandHeld"
+
+    # Rain Head, Overhead shower, Rain Shower, Rain Spout, Rain Faucet
+    SHOWER_RAIN_HEAD = "Alexa.Shower.RainHead"
+
+    # Degrees, Degree
+    UNIT_ANGLE_DEGREES = "Alexa.Unit.Angle.Degrees"
+
+    # Radians, Radian
+    UNIT_ANGLE_RADIANS = "Alexa.Unit.Angle.Radians"
+
+    # Feet, Foot
+    UNIT_DISTANCE_FEET = "Alexa.Unit.Distance.Feet"
+
+    # Inches, Inch
+    UNIT_DISTANCE_INCHES = "Alexa.Unit.Distance.Inches"
+
+    # Kilometers
+    UNIT_DISTANCE_KILOMETERS = "Alexa.Unit.Distance.Kilometers"
+
+    # Meters, Meter, m
+    UNIT_DISTANCE_METERS = "Alexa.Unit.Distance.Meters"
+
+    # Miles, Mile
+    UNIT_DISTANCE_MILES = "Alexa.Unit.Distance.Miles"
+
+    # Yards, Yard
+    UNIT_DISTANCE_YARDS = "Alexa.Unit.Distance.Yards"
+
+    # Grams, Gram, g
+    UNIT_MASS_GRAMS = "Alexa.Unit.Mass.Grams"
+
+    # Kilograms, Kilogram, kg
+    UNIT_MASS_KILOGRAMS = "Alexa.Unit.Mass.Kilograms"
+
+    # Percent
+    UNIT_PERCENT = "Alexa.Unit.Percent"
+
+    # Celsius, Degrees Celsius, Degrees, C, Centigrade, Degrees Centigrade
+    UNIT_TEMPERATURE_CELSIUS = "Alexa.Unit.Temperature.Celsius"
+
+    # Degrees, Degree
+    UNIT_TEMPERATURE_DEGREES = "Alexa.Unit.Temperature.Degrees"
+
+    # Fahrenheit, Degrees Fahrenheit, Degrees F, Degrees, F
+    UNIT_TEMPERATURE_FAHRENHEIT = "Alexa.Unit.Temperature.Fahrenheit"
+
+    # Kelvin, Degrees Kelvin, Degrees K, Degrees, K
+    UNIT_TEMPERATURE_KELVIN = "Alexa.Unit.Temperature.Kelvin"
+
+    # Cubic Feet, Cubic Foot
+    UNIT_VOLUME_CUBIC_FEET = "Alexa.Unit.Volume.CubicFeet"
+
+    # Cubic Meters, Cubic Meter, Meters Cubed
+    UNIT_VOLUME_CUBIC_METERS = "Alexa.Unit.Volume.CubicMeters"
+
+    # Gallons, Gallon
+    UNIT_VOLUME_GALLONS = "Alexa.Unit.Volume.Gallons"
+
+    # Liters, Liter, L
+    UNIT_VOLUME_LITERS = "Alexa.Unit.Volume.Liters"
+
+    # Pints, Pint
+    UNIT_VOLUME_PINTS = "Alexa.Unit.Volume.Pints"
+
+    # Quarts, Quart
+    UNIT_VOLUME_QUARTS = "Alexa.Unit.Volume.Quarts"
+
+    # Ounces, Ounce, oz
+    UNIT_WEIGHT_OUNCES = "Alexa.Unit.Weight.Ounces"
+
+    # Pounds, Pound, lbs
+    UNIT_WEIGHT_POUNDS = "Alexa.Unit.Weight.Pounds"
+
+    # Close
+    VALUE_CLOSE = "Alexa.Value.Close"
+
+    # Delicates, Delicate
+    VALUE_DELICATE = "Alexa.Value.Delicate"
+
+    # High
+    VALUE_HIGH = "Alexa.Value.High"
+
+    # Low
+    VALUE_LOW = "Alexa.Value.Low"
+
+    # Maximum, Max
+    VALUE_MAXIMUM = "Alexa.Value.Maximum"
+
+    # Medium, Mid
+    VALUE_MEDIUM = "Alexa.Value.Medium"
+
+    # Minimum, Min
+    VALUE_MINIMUM = "Alexa.Value.Minimum"
+
+    # Open
+    VALUE_OPEN = "Alexa.Value.Open"
+
+    # Quick Wash, Fast Wash, Wash Quickly, Speed Wash
+    VALUE_QUICK_WASH = "Alexa.Value.QuickWash"
+
+
+class AlexaCapabilityResource:
+    """Base class for Alexa capabilityResources, ModeResources, and presetResources objects.
+
+    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
+    """
+
+    def __init__(self, labels):
+        """Initialize an Alexa resource."""
+        self._resource_labels = []
+        for label in labels:
+            self._resource_labels.append(label)
+
+    def serialize_capability_resources(self):
+        """Return capabilityResources object serialized for an API response."""
+        return self.serialize_labels(self._resource_labels)
+
+    @staticmethod
+    def serialize_configuration():
+        """Return ModeResources, PresetResources friendlyNames serialized for an API response."""
+        return []
+
+    @staticmethod
+    def serialize_labels(resources):
+        """Return resource label objects for friendlyNames serialized for an API response."""
+        labels = []
+        for label in resources:
+            if label in AlexaGlobalCatalog.__dict__.values():
+                label = {"@type": "asset", "value": {"assetId": label}}
+            else:
+                label = {"@type": "text", "value": {"text": label, "locale": "en-US"}}
+
+            labels.append(label)
+
+        return {"friendlyNames": labels}
+
+
+class AlexaModeResource(AlexaCapabilityResource):
+    """Implements Alexa ModeResources.
+
+    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
+    """
+
+    def __init__(self, labels, ordered=False):
+        """Initialize an Alexa modeResource."""
+        super().__init__(labels)
+        self._supported_modes = []
+        self._mode_ordered = ordered
+
+    def add_mode(self, value, labels):
+        """Add mode to the supportedModes object."""
+        self._supported_modes.append({"value": value, "labels": labels})
+
+    def serialize_configuration(self):
+        """Return configuration for ModeResources friendlyNames serialized for an API response."""
+        mode_resources = []
+        for mode in self._supported_modes:
+            result = {
+                "value": mode["value"],
+                "modeResources": self.serialize_labels(mode["labels"]),
+            }
+            mode_resources.append(result)
+
+        return {"ordered": self._mode_ordered, "supportedModes": mode_resources}
+
+
+class AlexaPresetResource(AlexaCapabilityResource):
+    """Implements Alexa PresetResources.
+
+    Use presetResources with RangeController to provide a set of friendlyNames for each RangeController preset.
+
+    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#presetresources
+    """
+
+    def __init__(self, labels, min_value, max_value, precision, unit=None):
+        """Initialize an Alexa presetResource."""
+        super().__init__(labels)
+        self._presets = []
+        self._minimum_value = int(min_value)
+        self._maximum_value = int(max_value)
+        self._precision = int(precision)
+        self._unit_of_measure = None
+        if unit in AlexaGlobalCatalog.__dict__.values():
+            self._unit_of_measure = unit
+
+    def add_preset(self, value, labels):
+        """Add preset to configuration presets array."""
+        self._presets.append({"value": value, "labels": labels})
+
+    def serialize_configuration(self):
+        """Return configuration for PresetResources friendlyNames serialized for an API response."""
+        configuration = {
+            "supportedRange": {
+                "minimumValue": self._minimum_value,
+                "maximumValue": self._maximum_value,
+                "precision": self._precision,
+            }
+        }
+
+        if self._unit_of_measure:
+            configuration["unitOfMeasure"] = self._unit_of_measure
+
+        if self._presets:
+            preset_resources = []
+            for preset in self._presets:
+                preset_resources.append(
+                    {
+                        "rangeValue": preset["value"],
+                        "presetResources": self.serialize_labels(preset["labels"]),
+                    }
+                )
+            configuration["presets"] = preset_resources
+
+        return configuration
+
+
+class AlexaSemantics:
+    """Class for Alexa Semantics Object.
+
+    You can optionally enable additional utterances by using semantics. When you use semantics,
+    you manually map the phrases "open", "close", "raise", and "lower" to directives.
+
+    Semantics is supported for the following interfaces only: ModeController, RangeController, and ToggleController.
+
+    https://developer.amazon.com/docs/device-apis/alexa-discovery.html#semantics-object
+    """
+
+    MAPPINGS_ACTION = "actionMappings"
+    MAPPINGS_STATE = "stateMappings"
+
+    ACTIONS_TO_DIRECTIVE = "ActionsToDirective"
+    STATES_TO_VALUE = "StatesToValue"
+    STATES_TO_RANGE = "StatesToRange"
+
+    ACTION_CLOSE = "Alexa.Actions.Close"
+    ACTION_LOWER = "Alexa.Actions.Lower"
+    ACTION_OPEN = "Alexa.Actions.Open"
+    ACTION_RAISE = "Alexa.Actions.Raise"
+
+    STATES_OPEN = "Alexa.States.Open"
+    STATES_CLOSED = "Alexa.States.Closed"
+
+    DIRECTIVE_RANGE_SET_VALUE = "SetRangeValue"
+    DIRECTIVE_RANGE_ADJUST_VALUE = "AdjustRangeValue"
+    DIRECTIVE_TOGGLE_TURN_ON = "TurnOn"
+    DIRECTIVE_TOGGLE_TURN_OFF = "TurnOff"
+    DIRECTIVE_MODE_SET_MODE = "SetMode"
+    DIRECTIVE_MODE_ADJUST_MODE = "AdjustMode"
+
+    def __init__(self):
+        """Initialize an Alexa modeResource."""
+        self._action_mappings = []
+        self._state_mappings = []
+
+    def _add_action_mapping(self, semantics):
+        """Add action mapping between actions and interface directives."""
+        self._action_mappings.append(semantics)
+
+    def _add_state_mapping(self, semantics):
+        """Add state mapping between states and interface directives."""
+        self._state_mappings.append(semantics)
+
+    def add_states_to_value(self, states, value):
+        """Add StatesToValue stateMappings."""
+        self._add_state_mapping(
+            {"@type": self.STATES_TO_VALUE, "states": states, "value": value}
+        )
+
+    def add_states_to_range(self, states, min_value, max_value):
+        """Add StatesToRange stateMappings."""
+        self._add_state_mapping(
+            {
+                "@type": self.STATES_TO_RANGE,
+                "states": states,
+                "range": {"minimumValue": min_value, "maximumValue": max_value},
+            }
+        )
+
+    def add_action_to_directive(self, actions, directive, payload):
+        """Add ActionsToDirective actionMappings."""
+        self._add_action_mapping(
+            {
+                "@type": self.ACTIONS_TO_DIRECTIVE,
+                "actions": actions,
+                "directive": {"name": directive, "payload": payload},
+            }
+        )
+
+    def serialize_semantics(self):
+        """Return semantics object serialized for an API response."""
+        semantics = {}
+        if self._action_mappings:
+            semantics[self.MAPPINGS_ACTION] = self._action_mappings
+        if self._state_mappings:
+            semantics[self.MAPPINGS_STATE] = self._state_mappings
+
+        return semantics
diff --git a/tests/components/alexa/test_capabilities.py b/tests/components/alexa/test_capabilities.py
index ab9c375103a..9c086e1fc50 100644
--- a/tests/components/alexa/test_capabilities.py
+++ b/tests/components/alexa/test_capabilities.py
@@ -411,14 +411,14 @@ async def test_report_fan_direction(hass):
     properties.assert_not_has_property("Alexa.ModeController", "mode")
 
     properties = await reported_properties(hass, "fan.reverse")
-    properties.assert_equal("Alexa.ModeController", "mode", "reverse")
+    properties.assert_equal("Alexa.ModeController", "mode", "direction.reverse")
 
     properties = await reported_properties(hass, "fan.forward")
-    properties.assert_equal("Alexa.ModeController", "mode", "forward")
+    properties.assert_equal("Alexa.ModeController", "mode", "direction.forward")
 
 
-async def test_report_cover_percentage_state(hass):
-    """Test PercentageController reports cover percentage correctly."""
+async def test_report_cover_range_value(hass):
+    """Test RangeController reports cover position correctly."""
     hass.states.async_set(
         "cover.fully_open",
         "open",
@@ -448,13 +448,13 @@ async def test_report_cover_percentage_state(hass):
     )
 
     properties = await reported_properties(hass, "cover.fully_open")
-    properties.assert_equal("Alexa.PercentageController", "percentage", 100)
+    properties.assert_equal("Alexa.RangeController", "rangeValue", 100)
 
     properties = await reported_properties(hass, "cover.half_open")
-    properties.assert_equal("Alexa.PercentageController", "percentage", 50)
+    properties.assert_equal("Alexa.RangeController", "rangeValue", 50)
 
     properties = await reported_properties(hass, "cover.closed")
-    properties.assert_equal("Alexa.PercentageController", "percentage", 0)
+    properties.assert_equal("Alexa.RangeController", "rangeValue", 0)
 
 
 async def test_report_climate_state(hass):
diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py
index 25c8f2a864f..4187c4a2c4f 100644
--- a/tests/components/alexa/test_smart_home.py
+++ b/tests/components/alexa/test_smart_home.py
@@ -126,10 +126,12 @@ async def discovery_test(device, hass, expected_endpoints=1):
     return None
 
 
-def get_capability(capabilities, capability_name):
+def get_capability(capabilities, capability_name, instance=None):
     """Search a set of capabilities for a specific one."""
     for capability in capabilities:
-        if capability["interface"] == capability_name:
+        if instance and capability["instance"] == instance:
+            return capability
+        elif capability["interface"] == capability_name:
             return capability
 
     return None
@@ -420,9 +422,29 @@ async def test_variable_fan(hass):
     )
     assert call.data["speed"] == "medium"
 
+    call, _ = await assert_request_calls_service(
+        "Alexa.PercentageController",
+        "SetPercentage",
+        "fan#test_2",
+        "fan.set_speed",
+        hass,
+        payload={"percentage": "33"},
+    )
+    assert call.data["speed"] == "low"
+
+    call, _ = await assert_request_calls_service(
+        "Alexa.PercentageController",
+        "SetPercentage",
+        "fan#test_2",
+        "fan.set_speed",
+        hass,
+        payload={"percentage": "100"},
+    )
+    assert call.data["speed"] == "high"
+
     await assert_percentage_changes(
         hass,
-        [("high", "-5"), ("off", "5"), ("low", "-80")],
+        [("high", "-5"), ("off", "5"), ("low", "-80"), ("medium", "-34")],
         "Alexa.PercentageController",
         "AdjustPercentage",
         "fan#test_2",
@@ -431,6 +453,16 @@ async def test_variable_fan(hass):
         "speed",
     )
 
+    call, _ = await assert_request_calls_service(
+        "Alexa.PowerLevelController",
+        "SetPowerLevel",
+        "fan#test_2",
+        "fan.set_speed",
+        hass,
+        payload={"powerLevel": "20"},
+    )
+    assert call.data["speed"] == "low"
+
     call, _ = await assert_request_calls_service(
         "Alexa.PowerLevelController",
         "SetPowerLevel",
@@ -441,6 +473,16 @@ async def test_variable_fan(hass):
     )
     assert call.data["speed"] == "medium"
 
+    call, _ = await assert_request_calls_service(
+        "Alexa.PowerLevelController",
+        "SetPowerLevel",
+        "fan#test_2",
+        "fan.set_speed",
+        hass,
+        payload={"powerLevel": "99"},
+    )
+    assert call.data["speed"] == "high"
+
     await assert_percentage_changes(
         hass,
         [("high", "-5"), ("medium", "-50"), ("low", "-80")],
@@ -1333,51 +1375,106 @@ async def test_group(hass):
     )
 
 
-async def test_cover(hass):
-    """Test cover discovery."""
+async def test_cover_position_range(hass):
+    """Test cover discovery and position using rangeController."""
     device = (
-        "cover.test",
-        "off",
-        {"friendly_name": "Test cover", "supported_features": 255, "position": 30},
+        "cover.test_range",
+        "open",
+        {
+            "friendly_name": "Test cover range",
+            "device_class": "blind",
+            "supported_features": 7,
+            "position": 30,
+        },
     )
     appliance = await discovery_test(device, hass)
 
-    assert appliance["endpointId"] == "cover#test"
-    assert appliance["displayCategories"][0] == "OTHER"
-    assert appliance["friendlyName"] == "Test cover"
+    assert appliance["endpointId"] == "cover#test_range"
+    assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
+    assert appliance["friendlyName"] == "Test cover range"
 
-    assert_endpoint_capabilities(
-        appliance,
-        "Alexa.ModeController",
-        "Alexa.PercentageController",
-        "Alexa.PowerController",
-        "Alexa.EndpointHealth",
-        "Alexa",
+    capabilities = assert_endpoint_capabilities(
+        appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
     )
 
-    await assert_power_controller_works(
-        "cover#test", "cover.open_cover", "cover.close_cover", hass
-    )
+    range_capability = get_capability(capabilities, "Alexa.RangeController")
+    assert range_capability is not None
+    assert range_capability["instance"] == "cover.position"
+
+    properties = range_capability["properties"]
+    assert properties["nonControllable"] is False
+    assert {"name": "rangeValue"} in properties["supported"]
+
+    capability_resources = range_capability["capabilityResources"]
+    assert capability_resources is not None
+    assert {
+        "@type": "text",
+        "value": {"text": "Position", "locale": "en-US"},
+    } in capability_resources["friendlyNames"]
+
+    assert {
+        "@type": "asset",
+        "value": {"assetId": "Alexa.Setting.Opening"},
+    } in capability_resources["friendlyNames"]
+
+    configuration = range_capability["configuration"]
+    assert configuration is not None
+    assert configuration["unitOfMeasure"] == "Alexa.Unit.Percent"
+
+    supported_range = configuration["supportedRange"]
+    assert supported_range["minimumValue"] == 0
+    assert supported_range["maximumValue"] == 100
+    assert supported_range["precision"] == 1
 
     call, _ = await assert_request_calls_service(
-        "Alexa.PercentageController",
-        "SetPercentage",
-        "cover#test",
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_range",
         "cover.set_cover_position",
         hass,
-        payload={"percentage": "50"},
+        payload={"rangeValue": "50"},
+        instance="cover.position",
     )
     assert call.data["position"] == 50
 
-    await assert_percentage_changes(
+    call, msg = await assert_request_calls_service(
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_range",
+        "cover.close_cover",
         hass,
-        [(25, "-5"), (35, "5"), (0, "-80")],
-        "Alexa.PercentageController",
-        "AdjustPercentage",
-        "cover#test",
-        "percentageDelta",
+        payload={"rangeValue": "0"},
+        instance="cover.position",
+    )
+    properties = msg["context"]["properties"][0]
+    assert properties["name"] == "rangeValue"
+    assert properties["namespace"] == "Alexa.RangeController"
+    assert properties["value"] == 0
+
+    call, msg = await assert_request_calls_service(
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_range",
+        "cover.open_cover",
+        hass,
+        payload={"rangeValue": "100"},
+        instance="cover.position",
+    )
+    properties = msg["context"]["properties"][0]
+    assert properties["name"] == "rangeValue"
+    assert properties["namespace"] == "Alexa.RangeController"
+    assert properties["value"] == 100
+
+    await assert_range_changes(
+        hass,
+        [(25, "-5"), (35, "5"), (0, "-99"), (100, "99")],
+        "Alexa.RangeController",
+        "AdjustRangeValue",
+        "cover#test_range",
+        False,
         "cover.set_cover_position",
         "position",
+        instance="cover.position",
     )
 
 
@@ -2292,26 +2389,25 @@ async def test_mode_unsupported_domain(hass):
     assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
 
 
-async def test_cover_position(hass):
-    """Test cover position mode discovery."""
+async def test_cover_position_mode(hass):
+    """Test cover discovery and position using modeController."""
     device = (
-        "cover.test",
-        "off",
-        {"friendly_name": "Test cover", "supported_features": 255, "position": 30},
+        "cover.test_mode",
+        "open",
+        {
+            "friendly_name": "Test cover mode",
+            "device_class": "blind",
+            "supported_features": 3,
+        },
     )
     appliance = await discovery_test(device, hass)
 
-    assert appliance["endpointId"] == "cover#test"
-    assert appliance["displayCategories"][0] == "OTHER"
-    assert appliance["friendlyName"] == "Test cover"
+    assert appliance["endpointId"] == "cover#test_mode"
+    assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
+    assert appliance["friendlyName"] == "Test cover mode"
 
     capabilities = assert_endpoint_capabilities(
-        appliance,
-        "Alexa",
-        "Alexa.ModeController",
-        "Alexa.PercentageController",
-        "Alexa.PowerController",
-        "Alexa.EndpointHealth",
+        appliance, "Alexa", "Alexa.ModeController", "Alexa.EndpointHealth"
     )
 
     mode_capability = get_capability(capabilities, "Alexa.ModeController")
@@ -2324,9 +2420,14 @@ async def test_cover_position(hass):
 
     capability_resources = mode_capability["capabilityResources"]
     assert capability_resources is not None
+    assert {
+        "@type": "text",
+        "value": {"text": "Position", "locale": "en-US"},
+    } in capability_resources["friendlyNames"]
+
     assert {
         "@type": "asset",
-        "value": {"assetId": "Alexa.Setting.Mode"},
+        "value": {"assetId": "Alexa.Setting.Opening"},
     } in capability_resources["friendlyNames"]
 
     configuration = mode_capability["configuration"]
@@ -2339,10 +2440,7 @@ async def test_cover_position(hass):
         "value": "position.open",
         "modeResources": {
             "friendlyNames": [
-                {"@type": "text", "value": {"text": "open", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "opened", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "raise", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "raised", "locale": "en-US"}},
+                {"@type": "asset", "value": {"assetId": "Alexa.Value.Open"}}
             ]
         },
     } in supported_modes
@@ -2350,19 +2448,24 @@ async def test_cover_position(hass):
         "value": "position.closed",
         "modeResources": {
             "friendlyNames": [
-                {"@type": "text", "value": {"text": "close", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "closed", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "shut", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "lower", "locale": "en-US"}},
-                {"@type": "text", "value": {"text": "lowered", "locale": "en-US"}},
+                {"@type": "asset", "value": {"assetId": "Alexa.Value.Close"}}
             ]
         },
     } in supported_modes
 
+    semantics = mode_capability["semantics"]
+    assert semantics is not None
+
+    action_mappings = semantics["actionMappings"]
+    assert action_mappings is not None
+
+    state_mappings = semantics["stateMappings"]
+    assert state_mappings is not None
+
     call, msg = await assert_request_calls_service(
         "Alexa.ModeController",
         "SetMode",
-        "cover#test",
+        "cover#test_mode",
         "cover.close_cover",
         hass,
         payload={"mode": "position.closed"},
@@ -2376,7 +2479,7 @@ async def test_cover_position(hass):
     call, msg = await assert_request_calls_service(
         "Alexa.ModeController",
         "SetMode",
-        "cover#test",
+        "cover#test_mode",
         "cover.open_cover",
         hass,
         payload={"mode": "position.open"},
@@ -2387,6 +2490,20 @@ async def test_cover_position(hass):
     assert properties["namespace"] == "Alexa.ModeController"
     assert properties["value"] == "position.open"
 
+    call, msg = await assert_request_calls_service(
+        "Alexa.ModeController",
+        "SetMode",
+        "cover#test_mode",
+        "cover.stop_cover",
+        hass,
+        payload={"mode": "position.custom"},
+        instance="cover.position",
+    )
+    properties = msg["context"]["properties"][0]
+    assert properties["name"] == "mode"
+    assert properties["namespace"] == "Alexa.ModeController"
+    assert properties["value"] == "position.custom"
+
 
 async def test_image_processing(hass):
     """Test image_processing discovery as event detection."""
@@ -2467,3 +2584,159 @@ async def test_presence_sensor(hass):
     assert properties["proactivelyReported"] is True
     assert not properties["retrievable"]
     assert {"name": "humanPresenceDetectionState"} in properties["supported"]
+
+
+async def test_cover_tilt_position_range(hass):
+    """Test cover discovery and tilt position using rangeController."""
+    device = (
+        "cover.test_tilt_range",
+        "open",
+        {
+            "friendly_name": "Test cover tilt range",
+            "device_class": "blind",
+            "supported_features": 240,
+            "tilt_position": 30,
+        },
+    )
+    appliance = await discovery_test(device, hass)
+
+    assert appliance["endpointId"] == "cover#test_tilt_range"
+    assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
+    assert appliance["friendlyName"] == "Test cover tilt range"
+
+    capabilities = assert_endpoint_capabilities(
+        appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
+    )
+
+    range_capability = get_capability(capabilities, "Alexa.RangeController")
+    assert range_capability is not None
+    assert range_capability["instance"] == "cover.tilt_position"
+
+    semantics = range_capability["semantics"]
+    assert semantics is not None
+
+    action_mappings = semantics["actionMappings"]
+    assert action_mappings is not None
+
+    state_mappings = semantics["stateMappings"]
+    assert state_mappings is not None
+
+    call, _ = await assert_request_calls_service(
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_tilt_range",
+        "cover.set_cover_tilt_position",
+        hass,
+        payload={"rangeValue": "50"},
+        instance="cover.tilt_position",
+    )
+    assert call.data["position"] == 50
+
+    call, msg = await assert_request_calls_service(
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_tilt_range",
+        "cover.close_cover_tilt",
+        hass,
+        payload={"rangeValue": "0"},
+        instance="cover.tilt_position",
+    )
+    properties = msg["context"]["properties"][0]
+    assert properties["name"] == "rangeValue"
+    assert properties["namespace"] == "Alexa.RangeController"
+    assert properties["value"] == 0
+
+    call, msg = await assert_request_calls_service(
+        "Alexa.RangeController",
+        "SetRangeValue",
+        "cover#test_tilt_range",
+        "cover.open_cover_tilt",
+        hass,
+        payload={"rangeValue": "100"},
+        instance="cover.tilt_position",
+    )
+    properties = msg["context"]["properties"][0]
+    assert properties["name"] == "rangeValue"
+    assert properties["namespace"] == "Alexa.RangeController"
+    assert properties["value"] == 100
+
+    await assert_range_changes(
+        hass,
+        [(25, "-5"), (35, "5"), (0, "-99"), (100, "99")],
+        "Alexa.RangeController",
+        "AdjustRangeValue",
+        "cover#test_tilt_range",
+        False,
+        "cover.set_cover_tilt_position",
+        "tilt_position",
+        instance="cover.tilt_position",
+    )
+
+
+async def test_cover_semantics(hass):
+    """Test cover discovery and semantics."""
+    device = (
+        "cover.test_semantics",
+        "open",
+        {
+            "friendly_name": "Test cover semantics",
+            "device_class": "blind",
+            "supported_features": 255,
+            "position": 30,
+            "tilt_position": 30,
+        },
+    )
+    appliance = await discovery_test(device, hass)
+
+    assert appliance["endpointId"] == "cover#test_semantics"
+    assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
+    assert appliance["friendlyName"] == "Test cover semantics"
+
+    capabilities = assert_endpoint_capabilities(
+        appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
+    )
+
+    for range_instance in ("cover.position", "cover.tilt_position"):
+        range_capability = get_capability(
+            capabilities, "Alexa.RangeController", range_instance
+        )
+        semantics = range_capability["semantics"]
+        assert semantics is not None
+
+        action_mappings = semantics["actionMappings"]
+        assert action_mappings is not None
+        if range_instance == "cover.position":
+            assert {
+                "@type": "ActionsToDirective",
+                "actions": ["Alexa.Actions.Lower"],
+                "directive": {"name": "SetRangeValue", "payload": {"rangeValue": 0}},
+            } in action_mappings
+            assert {
+                "@type": "ActionsToDirective",
+                "actions": ["Alexa.Actions.Raise"],
+                "directive": {"name": "SetRangeValue", "payload": {"rangeValue": 100}},
+            } in action_mappings
+        elif range_instance == "cover.position":
+            assert {
+                "@type": "ActionsToDirective",
+                "actions": ["Alexa.Actions.Close"],
+                "directive": {"name": "SetRangeValue", "payload": {"rangeValue": 0}},
+            } in action_mappings
+            assert {
+                "@type": "ActionsToDirective",
+                "actions": ["Alexa.Actions.Open"],
+                "directive": {"name": "SetRangeValue", "payload": {"rangeValue": 100}},
+            } in action_mappings
+
+        state_mappings = semantics["stateMappings"]
+        assert state_mappings is not None
+        assert {
+            "@type": "StatesToValue",
+            "states": ["Alexa.States.Closed"],
+            "value": 0,
+        } in state_mappings
+        assert {
+            "@type": "StatesToRange",
+            "states": ["Alexa.States.Open"],
+            "range": {"minimumValue": 1, "maximumValue": 100},
+        } in state_mappings