mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
Drop support for fan speeds and support reverse (#53105)
This commit is contained in:
parent
fe89603ee7
commit
8a7cb389ed
@ -142,6 +142,7 @@ COMMAND_MEDIA_SEEK_RELATIVE = f"{PREFIX_COMMANDS}mediaSeekRelative"
|
|||||||
COMMAND_MEDIA_SEEK_TO_POSITION = f"{PREFIX_COMMANDS}mediaSeekToPosition"
|
COMMAND_MEDIA_SEEK_TO_POSITION = f"{PREFIX_COMMANDS}mediaSeekToPosition"
|
||||||
COMMAND_MEDIA_SHUFFLE = f"{PREFIX_COMMANDS}mediaShuffle"
|
COMMAND_MEDIA_SHUFFLE = f"{PREFIX_COMMANDS}mediaShuffle"
|
||||||
COMMAND_MEDIA_STOP = f"{PREFIX_COMMANDS}mediaStop"
|
COMMAND_MEDIA_STOP = f"{PREFIX_COMMANDS}mediaStop"
|
||||||
|
COMMAND_REVERSE = f"{PREFIX_COMMANDS}Reverse"
|
||||||
COMMAND_SET_HUMIDITY = f"{PREFIX_COMMANDS}SetHumidity"
|
COMMAND_SET_HUMIDITY = f"{PREFIX_COMMANDS}SetHumidity"
|
||||||
COMMAND_SELECT_CHANNEL = f"{PREFIX_COMMANDS}selectChannel"
|
COMMAND_SELECT_CHANNEL = f"{PREFIX_COMMANDS}selectChannel"
|
||||||
|
|
||||||
@ -1258,14 +1259,7 @@ class FanSpeedTrait(_Trait):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
name = TRAIT_FANSPEED
|
name = TRAIT_FANSPEED
|
||||||
commands = [COMMAND_FANSPEED]
|
commands = [COMMAND_FANSPEED, COMMAND_REVERSE]
|
||||||
|
|
||||||
speed_synonyms = {
|
|
||||||
fan.SPEED_OFF: ["stop", "off"],
|
|
||||||
fan.SPEED_LOW: ["slow", "low", "slowest", "lowest"],
|
|
||||||
fan.SPEED_MEDIUM: ["medium", "mid", "middle"],
|
|
||||||
fan.SPEED_HIGH: ["high", "max", "fast", "highest", "fastest", "maximum"],
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def supported(domain, features, device_class, _):
|
def supported(domain, features, device_class, _):
|
||||||
@ -1280,23 +1274,21 @@ class FanSpeedTrait(_Trait):
|
|||||||
"""Return speed point and modes attributes for a sync request."""
|
"""Return speed point and modes attributes for a sync request."""
|
||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
speeds = []
|
speeds = []
|
||||||
reversible = False
|
result = {}
|
||||||
|
|
||||||
if domain == fan.DOMAIN:
|
if domain == fan.DOMAIN:
|
||||||
# The use of legacy speeds is deprecated in the schema, support will be removed after a quarter (2021.7)
|
|
||||||
modes = self.state.attributes.get(fan.ATTR_SPEED_LIST, [])
|
|
||||||
for mode in modes:
|
|
||||||
speed = {
|
|
||||||
"speed_name": mode,
|
|
||||||
"speed_values": [
|
|
||||||
{"speed_synonym": self.speed_synonyms.get(mode), "lang": "en"}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
speeds.append(speed)
|
|
||||||
reversible = bool(
|
reversible = bool(
|
||||||
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||||
& fan.SUPPORT_DIRECTION
|
& fan.SUPPORT_DIRECTION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
result.update(
|
||||||
|
{
|
||||||
|
"reversible": reversible,
|
||||||
|
"supportsFanSpeedPercent": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
elif domain == climate.DOMAIN:
|
elif domain == climate.DOMAIN:
|
||||||
modes = self.state.attributes.get(climate.ATTR_FAN_MODES, [])
|
modes = self.state.attributes.get(climate.ATTR_FAN_MODES, [])
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
@ -1306,32 +1298,32 @@ class FanSpeedTrait(_Trait):
|
|||||||
}
|
}
|
||||||
speeds.append(speed)
|
speeds.append(speed)
|
||||||
|
|
||||||
return {
|
result.update(
|
||||||
|
{
|
||||||
|
"reversible": False,
|
||||||
"availableFanSpeeds": {"speeds": speeds, "ordered": True},
|
"availableFanSpeeds": {"speeds": speeds, "ordered": True},
|
||||||
"reversible": reversible,
|
|
||||||
"supportsFanSpeedPercent": True,
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def query_attributes(self):
|
def query_attributes(self):
|
||||||
"""Return speed point and modes query attributes."""
|
"""Return speed point and modes query attributes."""
|
||||||
|
|
||||||
attrs = self.state.attributes
|
attrs = self.state.attributes
|
||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
response = {}
|
response = {}
|
||||||
if domain == climate.DOMAIN:
|
if domain == climate.DOMAIN:
|
||||||
speed = attrs.get(climate.ATTR_FAN_MODE)
|
speed = attrs.get(climate.ATTR_FAN_MODE) or "off"
|
||||||
if speed is not None:
|
|
||||||
response["currentFanSpeedSetting"] = speed
|
response["currentFanSpeedSetting"] = speed
|
||||||
|
|
||||||
if domain == fan.DOMAIN:
|
if domain == fan.DOMAIN:
|
||||||
speed = attrs.get(fan.ATTR_SPEED)
|
|
||||||
percent = attrs.get(fan.ATTR_PERCENTAGE) or 0
|
percent = attrs.get(fan.ATTR_PERCENTAGE) or 0
|
||||||
if speed is not None:
|
|
||||||
response["on"] = speed != fan.SPEED_OFF
|
|
||||||
response["currentFanSpeedSetting"] = speed
|
|
||||||
if percent is not None:
|
|
||||||
response["currentFanSpeedPercent"] = percent
|
response["currentFanSpeedPercent"] = percent
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, command, data, params, challenge):
|
async def execute_fanspeed(self, data, params):
|
||||||
"""Execute an SetFanSpeed command."""
|
"""Execute an SetFanSpeed command."""
|
||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
if domain == climate.DOMAIN:
|
if domain == climate.DOMAIN:
|
||||||
@ -1345,25 +1337,43 @@ class FanSpeedTrait(_Trait):
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
context=data.context,
|
context=data.context,
|
||||||
)
|
)
|
||||||
if domain == fan.DOMAIN:
|
|
||||||
service_params = {
|
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
|
||||||
}
|
|
||||||
if "fanSpeedPercent" in params:
|
|
||||||
service = fan.SERVICE_SET_PERCENTAGE
|
|
||||||
service_params[fan.ATTR_PERCENTAGE] = params["fanSpeedPercent"]
|
|
||||||
else:
|
|
||||||
service = fan.SERVICE_SET_SPEED
|
|
||||||
service_params[fan.ATTR_SPEED] = params["fanSpeed"]
|
|
||||||
|
|
||||||
|
if domain == fan.DOMAIN:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
fan.DOMAIN,
|
fan.DOMAIN,
|
||||||
service,
|
fan.SERVICE_SET_PERCENTAGE,
|
||||||
service_params,
|
{
|
||||||
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
|
fan.ATTR_PERCENTAGE: params["fanSpeedPercent"],
|
||||||
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
context=data.context,
|
context=data.context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def execute_reverse(self, data, params):
|
||||||
|
"""Execute a Reverse command."""
|
||||||
|
domain = self.state.domain
|
||||||
|
if domain == fan.DOMAIN:
|
||||||
|
if self.state.attributes.get(fan.ATTR_DIRECTION) == fan.DIRECTION_FORWARD:
|
||||||
|
direction = fan.DIRECTION_REVERSE
|
||||||
|
else:
|
||||||
|
direction = fan.DIRECTION_FORWARD
|
||||||
|
|
||||||
|
await self.hass.services.async_call(
|
||||||
|
fan.DOMAIN,
|
||||||
|
fan.SERVICE_SET_DIRECTION,
|
||||||
|
{ATTR_ENTITY_ID: self.state.entity_id, fan.ATTR_DIRECTION: direction},
|
||||||
|
blocking=True,
|
||||||
|
context=data.context,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def execute(self, command, data, params, challenge):
|
||||||
|
"""Execute a smart home command."""
|
||||||
|
if command == COMMAND_FANSPEED:
|
||||||
|
await self.execute_fanspeed(data, params)
|
||||||
|
elif command == COMMAND_REVERSE:
|
||||||
|
await self.execute_reverse(data, params)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
class ModesTrait(_Trait):
|
class ModesTrait(_Trait):
|
||||||
|
@ -1461,13 +1461,6 @@ async def test_fan_speed(hass):
|
|||||||
"fan.living_room_fan",
|
"fan.living_room_fan",
|
||||||
fan.SPEED_HIGH,
|
fan.SPEED_HIGH,
|
||||||
attributes={
|
attributes={
|
||||||
"speed_list": [
|
|
||||||
fan.SPEED_OFF,
|
|
||||||
fan.SPEED_LOW,
|
|
||||||
fan.SPEED_MEDIUM,
|
|
||||||
fan.SPEED_HIGH,
|
|
||||||
],
|
|
||||||
"speed": "low",
|
|
||||||
"percentage": 33,
|
"percentage": 33,
|
||||||
"percentage_step": 1.0,
|
"percentage_step": 1.0,
|
||||||
},
|
},
|
||||||
@ -1476,64 +1469,14 @@ async def test_fan_speed(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert trt.sync_attributes() == {
|
assert trt.sync_attributes() == {
|
||||||
"availableFanSpeeds": {
|
|
||||||
"ordered": True,
|
|
||||||
"speeds": [
|
|
||||||
{
|
|
||||||
"speed_name": "off",
|
|
||||||
"speed_values": [{"speed_synonym": ["stop", "off"], "lang": "en"}],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"speed_name": "low",
|
|
||||||
"speed_values": [
|
|
||||||
{
|
|
||||||
"speed_synonym": ["slow", "low", "slowest", "lowest"],
|
|
||||||
"lang": "en",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"speed_name": "medium",
|
|
||||||
"speed_values": [
|
|
||||||
{"speed_synonym": ["medium", "mid", "middle"], "lang": "en"}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"speed_name": "high",
|
|
||||||
"speed_values": [
|
|
||||||
{
|
|
||||||
"speed_synonym": [
|
|
||||||
"high",
|
|
||||||
"max",
|
|
||||||
"fast",
|
|
||||||
"highest",
|
|
||||||
"fastest",
|
|
||||||
"maximum",
|
|
||||||
],
|
|
||||||
"lang": "en",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"reversible": False,
|
"reversible": False,
|
||||||
"supportsFanSpeedPercent": True,
|
"supportsFanSpeedPercent": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert trt.query_attributes() == {
|
assert trt.query_attributes() == {
|
||||||
"currentFanSpeedSetting": "low",
|
|
||||||
"on": True,
|
|
||||||
"currentFanSpeedPercent": 33,
|
"currentFanSpeedPercent": 33,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert trt.can_execute(trait.COMMAND_FANSPEED, params={"fanSpeed": "medium"})
|
|
||||||
|
|
||||||
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_SPEED)
|
|
||||||
await trt.execute(trait.COMMAND_FANSPEED, BASIC_DATA, {"fanSpeed": "medium"}, {})
|
|
||||||
|
|
||||||
assert len(calls) == 1
|
|
||||||
assert calls[0].data == {"entity_id": "fan.living_room_fan", "speed": "medium"}
|
|
||||||
|
|
||||||
assert trt.can_execute(trait.COMMAND_FANSPEED, params={"fanSpeedPercent": 10})
|
assert trt.can_execute(trait.COMMAND_FANSPEED, params={"fanSpeedPercent": 10})
|
||||||
|
|
||||||
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_PERCENTAGE)
|
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_PERCENTAGE)
|
||||||
@ -1543,6 +1486,53 @@ async def test_fan_speed(hass):
|
|||||||
assert calls[0].data == {"entity_id": "fan.living_room_fan", "percentage": 10}
|
assert calls[0].data == {"entity_id": "fan.living_room_fan", "percentage": 10}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"direction_state,direction_call",
|
||||||
|
[
|
||||||
|
(fan.DIRECTION_FORWARD, fan.DIRECTION_REVERSE),
|
||||||
|
(fan.DIRECTION_REVERSE, fan.DIRECTION_FORWARD),
|
||||||
|
(None, fan.DIRECTION_FORWARD),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_fan_reverse(hass, direction_state, direction_call):
|
||||||
|
"""Test FanSpeed trait speed control support for fan domain."""
|
||||||
|
|
||||||
|
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_DIRECTION)
|
||||||
|
|
||||||
|
trt = trait.FanSpeedTrait(
|
||||||
|
hass,
|
||||||
|
State(
|
||||||
|
"fan.living_room_fan",
|
||||||
|
fan.SPEED_HIGH,
|
||||||
|
attributes={
|
||||||
|
"percentage": 33,
|
||||||
|
"percentage_step": 1.0,
|
||||||
|
"direction": direction_state,
|
||||||
|
"supported_features": fan.SUPPORT_DIRECTION,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BASIC_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert trt.sync_attributes() == {
|
||||||
|
"reversible": True,
|
||||||
|
"supportsFanSpeedPercent": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert trt.query_attributes() == {
|
||||||
|
"currentFanSpeedPercent": 33,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert trt.can_execute(trait.COMMAND_REVERSE, params={})
|
||||||
|
await trt.execute(trait.COMMAND_REVERSE, BASIC_DATA, {}, {})
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data == {
|
||||||
|
"entity_id": "fan.living_room_fan",
|
||||||
|
"direction": direction_call,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_climate_fan_speed(hass):
|
async def test_climate_fan_speed(hass):
|
||||||
"""Test FanSpeed trait speed control support for climate domain."""
|
"""Test FanSpeed trait speed control support for climate domain."""
|
||||||
assert helpers.get_google_type(climate.DOMAIN, None) is not None
|
assert helpers.get_google_type(climate.DOMAIN, None) is not None
|
||||||
@ -1586,7 +1576,6 @@ async def test_climate_fan_speed(hass):
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"reversible": False,
|
"reversible": False,
|
||||||
"supportsFanSpeedPercent": True,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert trt.query_attributes() == {
|
assert trt.query_attributes() == {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user