Add support for variable fan speed list length. (#30574)

pull/30663/merge
ochlocracy 2020-01-10 20:26:37 -05:00 committed by Paulus Schoutsen
parent 669c89e8c0
commit 605b0ceb5f
6 changed files with 134 additions and 67 deletions

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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")

View File

@ -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",

View File

@ -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,
},
)