Add support for preset modes to bond fans (#64786)
parent
09408234a6
commit
74c16b977d
homeassistant/components/bond
tests/components/bond
|
@ -35,6 +35,8 @@ from .utils import BondDevice, BondHub
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PRESET_MODE_BREEZE = "Breeze"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
|
@ -74,11 +76,15 @@ class BondFan(BondEntity, FanEntity):
|
|||
self._power: bool | None = None
|
||||
self._speed: int | None = None
|
||||
self._direction: int | None = None
|
||||
if self._device.has_action(Action.BREEZE_ON):
|
||||
self._attr_preset_modes = [PRESET_MODE_BREEZE]
|
||||
|
||||
def _apply_state(self, state: dict) -> None:
|
||||
self._power = state.get("power")
|
||||
self._speed = state.get("speed")
|
||||
self._direction = state.get("direction")
|
||||
breeze = state.get("breeze", [0, 0, 0])
|
||||
self._attr_preset_mode = PRESET_MODE_BREEZE if breeze[0] else None
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
|
@ -185,13 +191,27 @@ class BondFan(BondEntity, FanEntity):
|
|||
"""Turn on the fan."""
|
||||
_LOGGER.debug("Fan async_turn_on called with percentage %s", percentage)
|
||||
|
||||
if percentage is not None:
|
||||
if preset_mode is not None:
|
||||
await self.async_set_preset_mode(preset_mode)
|
||||
elif percentage is not None:
|
||||
await self.async_set_percentage(percentage)
|
||||
else:
|
||||
await self._hub.bond.action(self._device.device_id, Action.turn_on())
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set the preset mode of the fan."""
|
||||
if preset_mode != PRESET_MODE_BREEZE or not self._device.has_action(
|
||||
Action.BREEZE_ON
|
||||
):
|
||||
raise ValueError(f"Invalid preset mode: {preset_mode}")
|
||||
await self._hub.bond.action(self._device.device_id, Action(Action.BREEZE_ON))
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the fan off."""
|
||||
if self.preset_mode == PRESET_MODE_BREEZE:
|
||||
await self._hub.bond.action(
|
||||
self._device.device_id, Action(Action.BREEZE_OFF)
|
||||
)
|
||||
await self._hub.bond.action(self._device.device_id, Action.turn_off())
|
||||
|
||||
async def async_set_direction(self, direction: str) -> None:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import call
|
||||
|
||||
from bond_api import Action, DeviceType, Direction
|
||||
import pytest
|
||||
|
@ -12,14 +13,18 @@ from homeassistant.components.bond.const import (
|
|||
DOMAIN as BOND_DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
||||
)
|
||||
from homeassistant.components.bond.fan import PRESET_MODE_BREEZE
|
||||
from homeassistant.components.fan import (
|
||||
ATTR_DIRECTION,
|
||||
ATTR_PRESET_MODE,
|
||||
ATTR_PRESET_MODES,
|
||||
ATTR_SPEED,
|
||||
ATTR_SPEED_LIST,
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_REVERSE,
|
||||
DOMAIN as FAN_DOMAIN,
|
||||
SERVICE_SET_DIRECTION,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
SERVICE_SET_SPEED,
|
||||
SPEED_OFF,
|
||||
)
|
||||
|
@ -49,14 +54,26 @@ def ceiling_fan(name: str):
|
|||
}
|
||||
|
||||
|
||||
def ceiling_fan_with_breeze(name: str):
|
||||
"""Create a ceiling fan with given name with breeze support."""
|
||||
return {
|
||||
"name": name,
|
||||
"type": DeviceType.CEILING_FAN,
|
||||
"actions": ["SetSpeed", "SetDirection", "BreezeOn"],
|
||||
}
|
||||
|
||||
|
||||
async def turn_fan_on(
|
||||
hass: core.HomeAssistant,
|
||||
fan_id: str,
|
||||
speed: str | None = None,
|
||||
percentage: int | None = None,
|
||||
preset_mode: str | None = None,
|
||||
) -> None:
|
||||
"""Turn the fan on at the specified speed."""
|
||||
service_data = {ATTR_ENTITY_ID: fan_id}
|
||||
if preset_mode:
|
||||
service_data[fan.ATTR_PRESET_MODE] = preset_mode
|
||||
if speed:
|
||||
service_data[fan.ATTR_SPEED] = speed
|
||||
if percentage:
|
||||
|
@ -205,6 +222,88 @@ async def test_turn_on_fan_with_percentage_6_speeds(hass: core.HomeAssistant):
|
|||
mock_set_speed.assert_called_with("test-device-id", Action.set_speed(6))
|
||||
|
||||
|
||||
async def test_turn_on_fan_preset_mode(hass: core.HomeAssistant):
|
||||
"""Tests that turn on command delegates to breeze on API."""
|
||||
await setup_platform(
|
||||
hass,
|
||||
FAN_DOMAIN,
|
||||
ceiling_fan_with_breeze("name-1"),
|
||||
bond_device_id="test-device-id",
|
||||
props={"max_speed": 6},
|
||||
)
|
||||
assert hass.states.get("fan.name_1").attributes[ATTR_PRESET_MODES] == [
|
||||
PRESET_MODE_BREEZE
|
||||
]
|
||||
|
||||
with patch_bond_action() as mock_set_preset_mode, patch_bond_device_state():
|
||||
await turn_fan_on(hass, "fan.name_1", preset_mode=PRESET_MODE_BREEZE)
|
||||
|
||||
mock_set_preset_mode.assert_called_with("test-device-id", Action(Action.BREEZE_ON))
|
||||
|
||||
with patch_bond_action() as mock_set_preset_mode, patch_bond_device_state():
|
||||
await hass.services.async_call(
|
||||
FAN_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
service_data={
|
||||
ATTR_PRESET_MODE: PRESET_MODE_BREEZE,
|
||||
ATTR_ENTITY_ID: "fan.name_1",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_set_preset_mode.assert_called_with("test-device-id", Action(Action.BREEZE_ON))
|
||||
|
||||
|
||||
async def test_turn_on_fan_preset_mode_not_supported(hass: core.HomeAssistant):
|
||||
"""Tests calling breeze mode on a fan that does not support it raises."""
|
||||
await setup_platform(
|
||||
hass,
|
||||
FAN_DOMAIN,
|
||||
ceiling_fan("name-1"),
|
||||
bond_device_id="test-device-id",
|
||||
props={"max_speed": 6},
|
||||
)
|
||||
|
||||
with patch_bond_action(), patch_bond_device_state(), pytest.raises(
|
||||
fan.NotValidPresetModeError
|
||||
):
|
||||
await turn_fan_on(hass, "fan.name_1", preset_mode=PRESET_MODE_BREEZE)
|
||||
|
||||
with patch_bond_action(), patch_bond_device_state(), pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
FAN_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
service_data={
|
||||
ATTR_PRESET_MODE: PRESET_MODE_BREEZE,
|
||||
ATTR_ENTITY_ID: "fan.name_1",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_turn_on_fan_with_off_with_breeze(hass: core.HomeAssistant):
|
||||
"""Tests that turn off command delegates to turn off API."""
|
||||
await setup_platform(
|
||||
hass,
|
||||
FAN_DOMAIN,
|
||||
ceiling_fan_with_breeze("name-1"),
|
||||
bond_device_id="test-device-id",
|
||||
state={"breeze": [1, 0, 0]},
|
||||
)
|
||||
|
||||
assert (
|
||||
hass.states.get("fan.name_1").attributes[ATTR_PRESET_MODE] == PRESET_MODE_BREEZE
|
||||
)
|
||||
|
||||
with patch_bond_action() as mock_actions, patch_bond_device_state():
|
||||
await turn_fan_on(hass, "fan.name_1", fan.SPEED_OFF)
|
||||
|
||||
assert mock_actions.mock_calls == [
|
||||
call("test-device-id", Action(Action.BREEZE_OFF)),
|
||||
call("test-device-id", Action.turn_off()),
|
||||
]
|
||||
|
||||
|
||||
async def test_turn_on_fan_without_speed(hass: core.HomeAssistant):
|
||||
"""Tests that turn on command delegates to turn on API."""
|
||||
await setup_platform(
|
||||
|
@ -218,7 +317,7 @@ async def test_turn_on_fan_without_speed(hass: core.HomeAssistant):
|
|||
|
||||
|
||||
async def test_turn_on_fan_with_off_speed(hass: core.HomeAssistant):
|
||||
"""Tests that turn on command delegates to turn off API."""
|
||||
"""Tests that turn off command delegates to turn off API."""
|
||||
await setup_platform(
|
||||
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue