diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index 047e981ab0d..f51809f52c4 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -19,6 +19,7 @@ from homeassistant.components import ( light, media_player, number, + remote, timer, vacuum, valve, @@ -439,6 +440,8 @@ class AlexaPowerController(AlexaCapability): is_on = self.entity.state == fan.STATE_ON elif self.entity.domain == humidifier.DOMAIN: is_on = self.entity.state == humidifier.STATE_ON + elif self.entity.domain == remote.DOMAIN: + is_on = self.entity.state == remote.STATE_ON elif self.entity.domain == vacuum.DOMAIN: is_on = self.entity.state == vacuum.STATE_CLEANING elif self.entity.domain == timer.DOMAIN: @@ -1436,6 +1439,12 @@ class AlexaModeController(AlexaCapability): if mode in modes: return f"{humidifier.ATTR_MODE}.{mode}" + # Remote Activity + if self.instance == f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}": + mode = self.entity.attributes.get(remote.ATTR_CURRENT_ACTIVITY, None) + if mode in self.entity.attributes.get(remote.ATTR_ACTIVITY_LIST, None): + return f"{remote.ATTR_ACTIVITY}.{mode}" + # Water heater operation mode if self.instance == f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}": operation_mode = self.entity.attributes.get( @@ -1550,6 +1559,22 @@ class AlexaModeController(AlexaCapability): ) return self._resource.serialize_capability_resources() + # Remote Resource + if self.instance == f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}": + self._resource = AlexaModeResource([AlexaGlobalCatalog.SETTING_MODE], False) + activities = self.entity.attributes.get(remote.ATTR_ACTIVITY_LIST) or [] + for activity in activities: + self._resource.add_mode( + f"{remote.ATTR_ACTIVITY}.{activity}", [activity] + ) + # Remotes with a single preset_mode completely break Alexa discovery, add a + # fake preset (see issue #53832). + if len(activities) == 1: + self._resource.add_mode( + f"{remote.ATTR_PRESET_MODE}.{PRESET_MODE_NA}", [PRESET_MODE_NA] + ) + return self._resource.serialize_capability_resources() + # Cover Position Resources if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": self._resource = AlexaModeResource( diff --git a/homeassistant/components/alexa/const.py b/homeassistant/components/alexa/const.py index 2c615b71166..4862e4d8a8c 100644 --- a/homeassistant/components/alexa/const.py +++ b/homeassistant/components/alexa/const.py @@ -88,7 +88,7 @@ API_THERMOSTAT_MODES_CUSTOM = { API_THERMOSTAT_PRESETS = {climate.PRESET_ECO: "ECO"} # AlexaModeController does not like a single mode for the fan preset or humidifier mode, -# we add PRESET_MODE_NA if a fan / humidifier has only one preset_mode +# we add PRESET_MODE_NA if a fan / humidifier / remote has only one preset_mode PRESET_MODE_NA = "-" STORAGE_ACCESS_TOKEN = "access_token" diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index 8d45ac3a11b..d757884b292 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -29,6 +29,7 @@ from homeassistant.components import ( lock, media_player, number, + remote, scene, script, sensor, @@ -647,6 +648,24 @@ class FanCapabilities(AlexaEntity): yield Alexa(self.entity) +@ENTITY_ADAPTERS.register(remote.DOMAIN) +class RemoteCapabilities(AlexaEntity): + """Class to represent Remote capabilities.""" + + def default_display_categories(self) -> list[str]: + """Return the display categories for this entity.""" + return [DisplayCategory.TV] + + def interfaces(self) -> Generator[AlexaCapability]: + """Yield the supported interfaces.""" + yield AlexaPowerController(self.entity) + yield AlexaModeController( + self.entity, instance=f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}" + ) + yield AlexaEndpointHealth(self.hass, self.entity) + yield Alexa(self.entity) + + @ENTITY_ADAPTERS.register(humidifier.DOMAIN) class HumidifierCapabilities(AlexaEntity): """Class to represent Humidifier capabilities.""" diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index 47e09db1166..03085550227 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -21,6 +21,7 @@ from homeassistant.components import ( light, media_player, number, + remote, timer, vacuum, valve, @@ -185,6 +186,8 @@ async def async_api_turn_on( service = fan.SERVICE_TURN_ON elif domain == humidifier.DOMAIN: service = humidifier.SERVICE_TURN_ON + elif domain == remote.DOMAIN: + service = remote.SERVICE_TURN_ON elif domain == vacuum.DOMAIN: supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) if ( @@ -234,6 +237,8 @@ async def async_api_turn_off( service = climate.SERVICE_TURN_OFF elif domain == fan.DOMAIN: service = fan.SERVICE_TURN_OFF + elif domain == remote.DOMAIN: + service = remote.SERVICE_TURN_OFF elif domain == humidifier.DOMAIN: service = humidifier.SERVICE_TURN_OFF elif domain == vacuum.DOMAIN: @@ -1200,6 +1205,17 @@ async def async_api_set_mode( msg = f"Entity '{entity.entity_id}' does not support Mode '{mode}'" raise AlexaInvalidValueError(msg) + # Remote Activity + if instance == f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}": + activity = mode.split(".")[1] + activities: list[str] | None = entity.attributes.get(remote.ATTR_ACTIVITY_LIST) + if activity != PRESET_MODE_NA and activities and preset_mode in activities: + service = remote.SERVICE_TURN_ON + data[remote.ATTR_ACTIVITY] = activity + else: + msg = f"Entity '{entity.entity_id}' does not support Mode '{mode}'" + raise AlexaInvalidValueError(msg) + # Water heater operation mode elif instance == f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}": operation_mode = mode.split(".")[1]