Add support for variable fan speed list length. (#30574)
parent
669c89e8c0
commit
605b0ceb5f
|
@ -31,7 +31,6 @@ from .const import (
|
|||
API_THERMOSTAT_PRESETS,
|
||||
DATE_FORMAT,
|
||||
PERCENTAGE_FAN_MAP,
|
||||
RANGE_FAN_MAP,
|
||||
Inputs,
|
||||
)
|
||||
from .errors import UnsupportedProperty
|
||||
|
@ -1273,8 +1272,12 @@ class AlexaRangeController(AlexaCapability):
|
|||
|
||||
# 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)
|
||||
speed_list = self.entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
speed = self.entity.attributes[fan.ATTR_SPEED]
|
||||
speed_index = next(
|
||||
(i for i, v in enumerate(speed_list) if v == speed), None
|
||||
)
|
||||
return speed_index
|
||||
|
||||
# Cover Position
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
|
@ -1302,24 +1305,22 @@ class AlexaRangeController(AlexaCapability):
|
|||
|
||||
# Fan Speed Resources
|
||||
if self.instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
speed_list = self.entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
max_value = len(speed_list) - 1
|
||||
self._resource = AlexaPresetResource(
|
||||
labels=[AlexaGlobalCatalog.SETTING_FAN_SPEED],
|
||||
min_value=1,
|
||||
max_value=3,
|
||||
min_value=0,
|
||||
max_value=max_value,
|
||||
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,
|
||||
],
|
||||
)
|
||||
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()
|
||||
|
||||
# Cover Position Resources
|
||||
|
|
|
@ -84,20 +84,6 @@ PERCENTAGE_FAN_MAP = {
|
|||
fan.SPEED_HIGH: 100,
|
||||
}
|
||||
|
||||
RANGE_FAN_MAP = {
|
||||
fan.SPEED_OFF: 0,
|
||||
fan.SPEED_LOW: 1,
|
||||
fan.SPEED_MEDIUM: 2,
|
||||
fan.SPEED_HIGH: 3,
|
||||
}
|
||||
|
||||
SPEED_FAN_MAP = {
|
||||
0: fan.SPEED_OFF,
|
||||
1: fan.SPEED_LOW,
|
||||
2: fan.SPEED_MEDIUM,
|
||||
3: fan.SPEED_HIGH,
|
||||
}
|
||||
|
||||
|
||||
class Cause:
|
||||
"""Possible causes for property changes.
|
||||
|
|
|
@ -51,8 +51,6 @@ from .const import (
|
|||
API_THERMOSTAT_MODES_CUSTOM,
|
||||
API_THERMOSTAT_PRESETS,
|
||||
PERCENTAGE_FAN_MAP,
|
||||
RANGE_FAN_MAP,
|
||||
SPEED_FAN_MAP,
|
||||
Cause,
|
||||
Inputs,
|
||||
)
|
||||
|
@ -1096,8 +1094,10 @@ async def async_api_set_range(hass, config, directive, context):
|
|||
|
||||
# Fan Speed
|
||||
if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
range_value = int(range_value)
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
speed = SPEED_FAN_MAP.get(int(range_value))
|
||||
speed_list = entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
speed = next((v for i, v in enumerate(speed_list) if i == range_value), None)
|
||||
|
||||
if not speed:
|
||||
msg = "Entity does not support value"
|
||||
|
@ -1174,9 +1174,16 @@ async def async_api_adjust_range(hass, config, directive, context):
|
|||
if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
range_delta = int(range_delta)
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
current_range = RANGE_FAN_MAP.get(entity.attributes.get(fan.ATTR_SPEED), 0)
|
||||
speed = SPEED_FAN_MAP.get(
|
||||
min(3, max(0, range_delta + current_range)), fan.SPEED_OFF
|
||||
speed_list = entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
current_speed = entity.attributes[fan.ATTR_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
|
||||
)
|
||||
|
||||
if speed == fan.SPEED_OFF:
|
||||
|
|
|
@ -315,12 +315,22 @@ async def test_report_fan_speed_state(hass):
|
|||
hass.states.async_set(
|
||||
"fan.off",
|
||||
"off",
|
||||
{"friendly_name": "Off fan", "speed": "off", "supported_features": 1},
|
||||
{
|
||||
"friendly_name": "Off fan",
|
||||
"speed": "off",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["off", "low", "medium", "high"],
|
||||
},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"fan.low_speed",
|
||||
"on",
|
||||
{"friendly_name": "Low speed fan", "speed": "low", "supported_features": 1},
|
||||
{
|
||||
"friendly_name": "Low speed fan",
|
||||
"speed": "low",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["off", "low", "medium", "high"],
|
||||
},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"fan.medium_speed",
|
||||
|
@ -329,12 +339,18 @@ async def test_report_fan_speed_state(hass):
|
|||
"friendly_name": "Medium speed fan",
|
||||
"speed": "medium",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["off", "low", "medium", "high"],
|
||||
},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"fan.high_speed",
|
||||
"on",
|
||||
{"friendly_name": "High speed fan", "speed": "high", "supported_features": 1},
|
||||
{
|
||||
"friendly_name": "High speed fan",
|
||||
"speed": "high",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["off", "low", "medium", "high"],
|
||||
},
|
||||
)
|
||||
|
||||
properties = await reported_properties(hass, "fan.off")
|
||||
|
@ -361,25 +377,24 @@ async def test_report_fan_speed_state(hass):
|
|||
async def test_report_fan_oscillating(hass):
|
||||
"""Test ToggleController reports fan oscillating correctly."""
|
||||
hass.states.async_set(
|
||||
"fan.off",
|
||||
"fan.oscillating_off",
|
||||
"off",
|
||||
{"friendly_name": "Off fan", "speed": "off", "supported_features": 3},
|
||||
{"friendly_name": "fan oscillating off", "supported_features": 2},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"fan.low_speed",
|
||||
"fan.oscillating_on",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Low speed fan",
|
||||
"speed": "low",
|
||||
"friendly_name": "Fan oscillating on",
|
||||
"oscillating": True,
|
||||
"supported_features": 3,
|
||||
"supported_features": 2,
|
||||
},
|
||||
)
|
||||
|
||||
properties = await reported_properties(hass, "fan.off")
|
||||
properties = await reported_properties(hass, "fan.oscillating_off")
|
||||
properties.assert_equal("Alexa.ToggleController", "toggleState", "OFF")
|
||||
|
||||
properties = await reported_properties(hass, "fan.low_speed")
|
||||
properties = await reported_properties(hass, "fan.oscillating_on")
|
||||
properties.assert_equal("Alexa.ToggleController", "toggleState", "ON")
|
||||
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ def get_capability(capabilities, capability_name, instance=None):
|
|||
for capability in capabilities:
|
||||
if instance and capability["instance"] == instance:
|
||||
return capability
|
||||
elif capability["interface"] == capability_name:
|
||||
if capability["interface"] == capability_name:
|
||||
return capability
|
||||
|
||||
return None
|
||||
|
@ -497,11 +497,11 @@ async def test_variable_fan(hass):
|
|||
|
||||
|
||||
async def test_oscillating_fan(hass):
|
||||
"""Test oscillating fan discovery."""
|
||||
"""Test oscillating fan with ToggleController."""
|
||||
device = (
|
||||
"fan.test_3",
|
||||
"off",
|
||||
{"friendly_name": "Test fan 3", "supported_features": 3},
|
||||
{"friendly_name": "Test fan 3", "supported_features": 2},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
|
||||
|
@ -510,10 +510,7 @@ async def test_oscillating_fan(hass):
|
|||
assert appliance["friendlyName"] == "Test fan 3"
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa.PercentageController",
|
||||
"Alexa.PowerController",
|
||||
"Alexa.PowerLevelController",
|
||||
"Alexa.RangeController",
|
||||
"Alexa.ToggleController",
|
||||
"Alexa.EndpointHealth",
|
||||
"Alexa",
|
||||
|
@ -558,13 +555,13 @@ async def test_oscillating_fan(hass):
|
|||
|
||||
|
||||
async def test_direction_fan(hass):
|
||||
"""Test direction fan discovery."""
|
||||
"""Test fan direction with modeController."""
|
||||
device = (
|
||||
"fan.test_4",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Test fan 4",
|
||||
"supported_features": 5,
|
||||
"supported_features": 4,
|
||||
"direction": "forward",
|
||||
},
|
||||
)
|
||||
|
@ -575,10 +572,7 @@ async def test_direction_fan(hass):
|
|||
assert appliance["friendlyName"] == "Test fan 4"
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa.PercentageController",
|
||||
"Alexa.PowerController",
|
||||
"Alexa.PowerLevelController",
|
||||
"Alexa.RangeController",
|
||||
"Alexa.ModeController",
|
||||
"Alexa.EndpointHealth",
|
||||
"Alexa",
|
||||
|
@ -667,17 +661,14 @@ async def test_direction_fan(hass):
|
|||
|
||||
|
||||
async def test_fan_range(hass):
|
||||
"""Test fan discovery with range controller.
|
||||
|
||||
This one has variable speed.
|
||||
"""
|
||||
"""Test fan speed with rangeController."""
|
||||
device = (
|
||||
"fan.test_5",
|
||||
"off",
|
||||
{
|
||||
"friendly_name": "Test fan 5",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["low", "medium", "high"],
|
||||
"speed_list": ["off", "low", "medium", "high", "turbo", "warp_speed"],
|
||||
"speed": "medium",
|
||||
},
|
||||
)
|
||||
|
@ -701,6 +692,60 @@ async def test_fan_range(hass):
|
|||
assert range_capability is not None
|
||||
assert range_capability["instance"] == "fan.speed"
|
||||
|
||||
capability_resources = range_capability["capabilityResources"]
|
||||
assert capability_resources is not None
|
||||
assert {
|
||||
"@type": "asset",
|
||||
"value": {"assetId": "Alexa.Setting.FanSpeed"},
|
||||
} in capability_resources["friendlyNames"]
|
||||
|
||||
configuration = range_capability["configuration"]
|
||||
assert configuration is not None
|
||||
|
||||
supported_range = configuration["supportedRange"]
|
||||
assert supported_range["minimumValue"] == 0
|
||||
assert supported_range["maximumValue"] == 5
|
||||
assert supported_range["precision"] == 1
|
||||
|
||||
presets = configuration["presets"]
|
||||
assert {
|
||||
"rangeValue": 0,
|
||||
"presetResources": {
|
||||
"friendlyNames": [
|
||||
{"@type": "text", "value": {"text": "off", "locale": "en-US"}}
|
||||
]
|
||||
},
|
||||
} in presets
|
||||
|
||||
assert {
|
||||
"rangeValue": 1,
|
||||
"presetResources": {
|
||||
"friendlyNames": [
|
||||
{"@type": "text", "value": {"text": "low", "locale": "en-US"}},
|
||||
{"@type": "asset", "value": {"assetId": "Alexa.Value.Minimum"}},
|
||||
]
|
||||
},
|
||||
} in presets
|
||||
|
||||
assert {
|
||||
"rangeValue": 2,
|
||||
"presetResources": {
|
||||
"friendlyNames": [
|
||||
{"@type": "text", "value": {"text": "medium", "locale": "en-US"}}
|
||||
]
|
||||
},
|
||||
} in presets
|
||||
|
||||
assert {
|
||||
"rangeValue": 5,
|
||||
"presetResources": {
|
||||
"friendlyNames": [
|
||||
{"@type": "text", "value": {"text": "warp speed", "locale": "en-US"}},
|
||||
{"@type": "asset", "value": {"assetId": "Alexa.Value.Maximum"}},
|
||||
]
|
||||
},
|
||||
} in presets
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.RangeController",
|
||||
"SetRangeValue",
|
||||
|
@ -712,9 +757,20 @@ async def test_fan_range(hass):
|
|||
)
|
||||
assert call.data["speed"] == "low"
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.RangeController",
|
||||
"SetRangeValue",
|
||||
"fan#test_5",
|
||||
"fan.set_speed",
|
||||
hass,
|
||||
payload={"rangeValue": "5"},
|
||||
instance="fan.speed",
|
||||
)
|
||||
assert call.data["speed"] == "warp_speed"
|
||||
|
||||
await assert_range_changes(
|
||||
hass,
|
||||
[("low", "-1"), ("high", "1"), ("medium", "0")],
|
||||
[("low", "-1"), ("high", "1"), ("medium", "0"), ("warp_speed", "99")],
|
||||
"Alexa.RangeController",
|
||||
"AdjustRangeValue",
|
||||
"fan#test_5",
|
||||
|
@ -733,7 +789,7 @@ async def test_fan_range_off(hass):
|
|||
{
|
||||
"friendly_name": "Test fan 6",
|
||||
"supported_features": 1,
|
||||
"speed_list": ["low", "medium", "high"],
|
||||
"speed_list": ["off", "low", "medium", "high"],
|
||||
"speed": "high",
|
||||
},
|
||||
)
|
||||
|
@ -752,7 +808,7 @@ async def test_fan_range_off(hass):
|
|||
|
||||
await assert_range_changes(
|
||||
hass,
|
||||
[("off", "-3")],
|
||||
[("off", "-3"), ("off", "-99")],
|
||||
"Alexa.RangeController",
|
||||
"AdjustRangeValue",
|
||||
"fan#test_6",
|
||||
|
|
|
@ -49,6 +49,7 @@ async def test_report_state_instance(hass, aioclient_mock):
|
|||
"friendly_name": "Test fan",
|
||||
"supported_features": 3,
|
||||
"speed": "off",
|
||||
"speed_list": ["off", "low", "high"],
|
||||
"oscillating": False,
|
||||
},
|
||||
)
|
||||
|
@ -62,6 +63,7 @@ async def test_report_state_instance(hass, aioclient_mock):
|
|||
"friendly_name": "Test fan",
|
||||
"supported_features": 3,
|
||||
"speed": "high",
|
||||
"speed_list": ["off", "low", "high"],
|
||||
"oscillating": True,
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue