Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion homeassistant/components/alexa/capabilities.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
"""Alexa capabilities."""
import logging

from homeassistant.components import cover, fan, image_processing, input_number, light
from homeassistant.components import (
cover,
fan,
image_processing,
input_number,
light,
vacuum,
)
from homeassistant.components.alarm_control_panel import ATTR_CODE_FORMAT, FORMAT_NUMBER
import homeassistant.components.climate.const as climate
import homeassistant.components.media_player.const as media_player
Expand Down Expand Up @@ -1291,6 +1298,15 @@ def get_property(self, name):
if self.instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
return float(self.entity.state)

# Vacuum Fan Speed
if self.instance == f"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
speed_list = self.entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
speed = self.entity.attributes[vacuum.ATTR_FAN_SPEED]
speed_index = next(
(i for i, v in enumerate(speed_list) if v == speed), None
)
return speed_index

return None

def configuration(self):
Expand Down Expand Up @@ -1367,6 +1383,26 @@ def capability_resources(self):
)
return self._resource.serialize_capability_resources()

# Vacuum Fan Speed Resources
if self.instance == f"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
speed_list = self.entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
max_value = len(speed_list) - 1
self._resource = AlexaPresetResource(
labels=[AlexaGlobalCatalog.SETTING_FAN_SPEED],
min_value=0,
max_value=max_value,
precision=1,
)
for index, speed in enumerate(speed_list):
labels = [speed.replace("_", " ")]
if index == 1:
labels.append(AlexaGlobalCatalog.VALUE_MINIMUM)
if index == max_value:
labels.append(AlexaGlobalCatalog.VALUE_MAXIMUM)
self._resource.add_preset(value=index, labels=labels)

return self._resource.serialize_capability_resources()

return None

def semantics(self):
Expand Down
32 changes: 32 additions & 0 deletions homeassistant/components/alexa/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
sensor,
switch,
timer,
vacuum,
)
from homeassistant.components.climate import const as climate
from homeassistant.const import (
Expand Down Expand Up @@ -724,3 +725,34 @@ def interfaces(self):
"""Yield the supported interfaces."""
yield AlexaTimeHoldController(self.entity, allow_remote_resume=True)
yield Alexa(self.entity)


@ENTITY_ADAPTERS.register(vacuum.DOMAIN)
class VacuumCapabilities(AlexaEntity):
"""Class to represent vacuum capabilities."""

def default_display_categories(self):
"""Return the display categories for this entity."""
return [DisplayCategory.OTHER]

def interfaces(self):
"""Yield the supported interfaces."""
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if (supported & vacuum.SUPPORT_TURN_ON) and (
supported & vacuum.SUPPORT_TURN_OFF
):
yield AlexaPowerController(self.entity)

if supported & vacuum.SUPPORT_FAN_SPEED:
yield AlexaRangeController(
self.entity, instance=f"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}"
)

if supported & vacuum.SUPPORT_PAUSE:
support_resume = bool(supported & vacuum.SUPPORT_START)
yield AlexaTimeHoldController(
self.entity, allow_remote_resume=support_resume
)

yield AlexaEndpointHealth(self.hass, self.entity)
yield Alexa(self.hass)
57 changes: 55 additions & 2 deletions homeassistant/components/alexa/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
light,
media_player,
timer,
vacuum,
)
from homeassistant.components.climate import const as climate
from homeassistant.const import (
Expand Down Expand Up @@ -1138,6 +1139,20 @@ async def async_api_set_range(hass, config, directive, context):
max_value = float(entity.attributes[input_number.ATTR_MAX])
data[input_number.ATTR_VALUE] = min(max_value, max(min_value, range_value))

# Vacuum Fan Speed
elif instance == f"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
service = vacuum.SERVICE_SET_FAN_SPEED
speed_list = entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
speed = next(
(v for i, v in enumerate(speed_list) if i == int(range_value)), None
)

if not speed:
msg = "Entity does not support value"
raise AlexaInvalidValueError(msg)

data[vacuum.ATTR_FAN_SPEED] = speed

else:
msg = "Entity does not support directive"
raise AlexaInvalidDirectiveError(msg)
Expand Down Expand Up @@ -1220,6 +1235,24 @@ async def async_api_adjust_range(hass, config, directive, context):
max_value, max(min_value, range_delta + current)
)

# Vacuum Fan Speed
elif instance == f"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
range_delta = int(range_delta)
service = vacuum.SERVICE_SET_FAN_SPEED
speed_list = entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
current_speed = entity.attributes[vacuum.ATTR_FAN_SPEED]
current_speed_index = next(
(i for i, v in enumerate(speed_list) if v == current_speed), 0
)
new_speed_index = min(
len(speed_list) - 1, max(0, current_speed_index + range_delta)
)
speed = next(
(v for i, v in enumerate(speed_list) if i == new_speed_index), None
)

data[vacuum.ATTR_FAN_SPEED] = response_value = speed

else:
msg = "Entity does not support directive"
raise AlexaInvalidDirectiveError(msg)
Expand Down Expand Up @@ -1412,8 +1445,18 @@ async def async_api_hold(hass, config, directive, context):
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}

if entity.domain == timer.DOMAIN:
service = timer.SERVICE_PAUSE

elif entity.domain == vacuum.DOMAIN:
service = vacuum.SERVICE_START_PAUSE

else:
msg = "Entity does not support directive"
raise AlexaInvalidDirectiveError(msg)

await hass.services.async_call(
entity.domain, timer.SERVICE_PAUSE, data, blocking=False, context=context
entity.domain, service, data, blocking=False, context=context
)

return directive.response()
Expand All @@ -1425,8 +1468,18 @@ async def async_api_resume(hass, config, directive, context):
entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id}

if entity.domain == timer.DOMAIN:
service = timer.SERVICE_START

elif entity.domain == vacuum.DOMAIN:
service = vacuum.SERVICE_START_PAUSE

else:
msg = "Entity does not support directive"
raise AlexaInvalidDirectiveError(msg)

await hass.services.async_call(
entity.domain, timer.SERVICE_START, data, blocking=False, context=context
entity.domain, service, data, blocking=False, context=context
)

return directive.response()
Loading