Fix alexa calling not featured cover services (#105444)

* Fix alexa calls not supported cover services

* Follow up comment and additional tests
This commit is contained in:
Jan Bouwhuis 2023-12-10 17:27:01 +01:00 committed by Franck Nijhof
parent 0023d66c80
commit 399f98a726
No known key found for this signature in database
GPG key ID: D62583BA8AB11CA3
2 changed files with 354 additions and 108 deletions

View file

@ -1304,13 +1304,14 @@ async def async_api_set_range(
service = None
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
range_value = directive.payload["rangeValue"]
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
# Cover Position
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
range_value = int(range_value)
if range_value == 0:
if supported & cover.CoverEntityFeature.CLOSE and range_value == 0:
service = cover.SERVICE_CLOSE_COVER
elif range_value == 100:
elif supported & cover.CoverEntityFeature.OPEN and range_value == 100:
service = cover.SERVICE_OPEN_COVER
else:
service = cover.SERVICE_SET_COVER_POSITION
@ -1319,9 +1320,9 @@ async def async_api_set_range(
# Cover Tilt
elif instance == f"{cover.DOMAIN}.tilt":
range_value = int(range_value)
if range_value == 0:
if supported & cover.CoverEntityFeature.CLOSE_TILT and range_value == 0:
service = cover.SERVICE_CLOSE_COVER_TILT
elif range_value == 100:
elif supported & cover.CoverEntityFeature.OPEN_TILT and range_value == 100:
service = cover.SERVICE_OPEN_COVER_TILT
else:
service = cover.SERVICE_SET_COVER_TILT_POSITION
@ -1332,13 +1333,11 @@ async def async_api_set_range(
range_value = int(range_value)
if range_value == 0:
service = fan.SERVICE_TURN_OFF
elif supported & fan.FanEntityFeature.SET_SPEED:
service = fan.SERVICE_SET_PERCENTAGE
data[fan.ATTR_PERCENTAGE] = range_value
else:
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported and fan.FanEntityFeature.SET_SPEED:
service = fan.SERVICE_SET_PERCENTAGE
data[fan.ATTR_PERCENTAGE] = range_value
else:
service = fan.SERVICE_TURN_ON
service = fan.SERVICE_TURN_ON
# Humidifier target humidity
elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":

View file

@ -6,7 +6,7 @@ import pytest
from homeassistant.components.alexa import smart_home, state_report
import homeassistant.components.camera as camera
from homeassistant.components.cover import CoverDeviceClass
from homeassistant.components.cover import CoverDeviceClass, CoverEntityFeature
from homeassistant.components.media_player import MediaPlayerEntityFeature
from homeassistant.components.vacuum import VacuumEntityFeature
from homeassistant.config import async_process_ha_core_config
@ -1884,8 +1884,199 @@ async def test_group(hass: HomeAssistant) -> None:
)
async def test_cover_position_range(hass: HomeAssistant) -> None:
@pytest.mark.parametrize(
("position", "position_attr_in_service_call", "supported_features", "service_call"),
[
(
30,
30,
CoverEntityFeature.SET_POSITION
| CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE,
"cover.set_cover_position",
),
(
0,
None,
CoverEntityFeature.SET_POSITION
| CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE,
"cover.close_cover",
),
(
99,
99,
CoverEntityFeature.SET_POSITION
| CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE,
"cover.set_cover_position",
),
(
100,
None,
CoverEntityFeature.SET_POSITION
| CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE,
"cover.open_cover",
),
(
0,
0,
CoverEntityFeature.SET_POSITION,
"cover.set_cover_position",
),
(
60,
60,
CoverEntityFeature.SET_POSITION,
"cover.set_cover_position",
),
(
100,
100,
CoverEntityFeature.SET_POSITION,
"cover.set_cover_position",
),
(
0,
0,
CoverEntityFeature.SET_POSITION | CoverEntityFeature.OPEN,
"cover.set_cover_position",
),
(
100,
100,
CoverEntityFeature.SET_POSITION | CoverEntityFeature.CLOSE,
"cover.set_cover_position",
),
],
ids=[
"position_30_open_close",
"position_0_open_close",
"position_99_open_close",
"position_100_open_close",
"position_0_no_open_close",
"position_60_no_open_close",
"position_100_no_open_close",
"position_0_no_close",
"position_100_no_open",
],
)
async def test_cover_position(
hass: HomeAssistant,
position: int,
position_attr_in_service_call: int | None,
supported_features: CoverEntityFeature,
service_call: str,
) -> None:
"""Test cover discovery and position using rangeController."""
device = (
"cover.test_range",
"open",
{
"friendly_name": "Test cover range",
"device_class": "blind",
"supported_features": supported_features,
"position": position,
},
)
appliance = await discovery_test(device, hass)
assert appliance["endpointId"] == "cover#test_range"
assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
assert appliance["friendlyName"] == "Test cover range"
capabilities = assert_endpoint_capabilities(
appliance,
"Alexa.PowerController",
"Alexa.RangeController",
"Alexa.EndpointHealth",
"Alexa",
)
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
# Assert for Position Semantics
position_semantics = range_capability["semantics"]
assert position_semantics is not None
position_action_mappings = position_semantics["actionMappings"]
assert position_action_mappings is not None
assert {
"@type": "ActionsToDirective",
"actions": ["Alexa.Actions.Lower", "Alexa.Actions.Close"],
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 0}},
} in position_action_mappings
assert {
"@type": "ActionsToDirective",
"actions": ["Alexa.Actions.Raise", "Alexa.Actions.Open"],
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 100}},
} in position_action_mappings
position_state_mappings = position_semantics["stateMappings"]
assert position_state_mappings is not None
assert {
"@type": "StatesToValue",
"states": ["Alexa.States.Closed"],
"value": 0,
} in position_state_mappings
assert {
"@type": "StatesToRange",
"states": ["Alexa.States.Open"],
"range": {"minimumValue": 1, "maximumValue": 100},
} in position_state_mappings
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"SetRangeValue",
"cover#test_range",
service_call,
hass,
payload={"rangeValue": position},
instance="cover.position",
)
assert call.data.get("position") == position_attr_in_service_call
properties = msg["context"]["properties"][0]
assert properties["name"] == "rangeValue"
assert properties["namespace"] == "Alexa.RangeController"
assert properties["value"] == position
async def test_cover_position_range(
hass: HomeAssistant,
) -> None:
"""Test cover discovery and position range using rangeController.
Also tests an invalid cover position being handled correctly.
"""
device = (
"cover.test_range",
"open",
@ -1969,59 +2160,6 @@ async def test_cover_position_range(hass: HomeAssistant) -> None:
"range": {"minimumValue": 1, "maximumValue": 100},
} in position_state_mappings
call, _ = await assert_request_calls_service(
"Alexa.RangeController",
"SetRangeValue",
"cover#test_range",
"cover.set_cover_position",
hass,
payload={"rangeValue": 50},
instance="cover.position",
)
assert call.data["position"] == 50
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"SetRangeValue",
"cover#test_range",
"cover.close_cover",
hass,
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
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"AdjustRangeValue",
"cover#test_range",
"cover.open_cover",
hass,
payload={"rangeValueDelta": 99, "rangeValueDeltaDefault": False},
instance="cover.position",
)
properties = msg["context"]["properties"][0]
assert properties["name"] == "rangeValue"
assert properties["namespace"] == "Alexa.RangeController"
assert properties["value"] == 100
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"AdjustRangeValue",
@ -3435,8 +3573,159 @@ async def test_presence_sensor(hass: HomeAssistant) -> None:
assert {"name": "humanPresenceDetectionState"} in properties["supported"]
async def test_cover_tilt_position_range(hass: HomeAssistant) -> None:
@pytest.mark.parametrize(
(
"tilt_position",
"tilt_position_attr_in_service_call",
"supported_features",
"service_call",
),
[
(
30,
30,
CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
| CoverEntityFeature.STOP_TILT,
"cover.set_cover_tilt_position",
),
(
0,
None,
CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
| CoverEntityFeature.STOP_TILT,
"cover.close_cover_tilt",
),
(
99,
99,
CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
| CoverEntityFeature.STOP_TILT,
"cover.set_cover_tilt_position",
),
(
100,
None,
CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
| CoverEntityFeature.STOP_TILT,
"cover.open_cover_tilt",
),
(
0,
0,
CoverEntityFeature.SET_TILT_POSITION,
"cover.set_cover_tilt_position",
),
(
60,
60,
CoverEntityFeature.SET_TILT_POSITION,
"cover.set_cover_tilt_position",
),
(
100,
100,
CoverEntityFeature.SET_TILT_POSITION,
"cover.set_cover_tilt_position",
),
(
0,
0,
CoverEntityFeature.SET_TILT_POSITION | CoverEntityFeature.OPEN_TILT,
"cover.set_cover_tilt_position",
),
(
100,
100,
CoverEntityFeature.SET_TILT_POSITION | CoverEntityFeature.CLOSE_TILT,
"cover.set_cover_tilt_position",
),
],
ids=[
"tilt_position_30_open_close",
"tilt_position_0_open_close",
"tilt_position_99_open_close",
"tilt_position_100_open_close",
"tilt_position_0_no_open_close",
"tilt_position_60_no_open_close",
"tilt_position_100_no_open_close",
"tilt_position_0_no_close",
"tilt_position_100_no_open",
],
)
async def test_cover_tilt_position(
hass: HomeAssistant,
tilt_position: int,
tilt_position_attr_in_service_call: int | None,
supported_features: CoverEntityFeature,
service_call: str,
) -> None:
"""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": supported_features,
"tilt_position": tilt_position,
},
)
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.PowerController",
"Alexa.RangeController",
"Alexa.EndpointHealth",
"Alexa",
)
range_capability = get_capability(capabilities, "Alexa.RangeController")
assert range_capability is not None
assert range_capability["instance"] == "cover.tilt"
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, msg = await assert_request_calls_service(
"Alexa.RangeController",
"SetRangeValue",
"cover#test_tilt_range",
service_call,
hass,
payload={"rangeValue": tilt_position},
instance="cover.tilt",
)
assert call.data.get("tilt_position") == tilt_position_attr_in_service_call
properties = msg["context"]["properties"][0]
assert properties["name"] == "rangeValue"
assert properties["namespace"] == "Alexa.RangeController"
assert properties["value"] == tilt_position
async def test_cover_tilt_position_range(hass: HomeAssistant) -> None:
"""Test cover discovery and tilt position range using rangeController.
Also tests and invalid tilt position being handled correctly.
"""
device = (
"cover.test_tilt_range",
"open",
@ -3485,48 +3774,6 @@ async def test_cover_tilt_position_range(hass: HomeAssistant) -> None:
)
assert call.data["tilt_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",
)
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",
)
properties = msg["context"]["properties"][0]
assert properties["name"] == "rangeValue"
assert properties["namespace"] == "Alexa.RangeController"
assert properties["value"] == 100
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"AdjustRangeValue",
"cover#test_tilt_range",
"cover.open_cover_tilt",
hass,
payload={"rangeValueDelta": 99, "rangeValueDeltaDefault": False},
instance="cover.tilt",
)
properties = msg["context"]["properties"][0]
assert properties["name"] == "rangeValue"
assert properties["namespace"] == "Alexa.RangeController"
assert properties["value"] == 100
call, msg = await assert_request_calls_service(
"Alexa.RangeController",
"AdjustRangeValue",