Add support for preset modes to bond fans (#64786)
This commit is contained in:
parent
09408234a6
commit
74c16b977d
2 changed files with 121 additions and 2 deletions
|
@ -35,6 +35,8 @@ from .utils import BondDevice, BondHub
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PRESET_MODE_BREEZE = "Breeze"
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -74,11 +76,15 @@ class BondFan(BondEntity, FanEntity):
|
||||||
self._power: bool | None = None
|
self._power: bool | None = None
|
||||||
self._speed: int | None = None
|
self._speed: int | None = None
|
||||||
self._direction: 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:
|
def _apply_state(self, state: dict) -> None:
|
||||||
self._power = state.get("power")
|
self._power = state.get("power")
|
||||||
self._speed = state.get("speed")
|
self._speed = state.get("speed")
|
||||||
self._direction = state.get("direction")
|
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
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
|
@ -185,13 +191,27 @@ class BondFan(BondEntity, FanEntity):
|
||||||
"""Turn on the fan."""
|
"""Turn on the fan."""
|
||||||
_LOGGER.debug("Fan async_turn_on called with percentage %s", percentage)
|
_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)
|
await self.async_set_percentage(percentage)
|
||||||
else:
|
else:
|
||||||
await self._hub.bond.action(self._device.device_id, Action.turn_on())
|
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:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the fan off."""
|
"""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())
|
await self._hub.bond.action(self._device.device_id, Action.turn_off())
|
||||||
|
|
||||||
async def async_set_direction(self, direction: str) -> None:
|
async def async_set_direction(self, direction: str) -> None:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from unittest.mock import call
|
||||||
|
|
||||||
from bond_api import Action, DeviceType, Direction
|
from bond_api import Action, DeviceType, Direction
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -12,14 +13,18 @@ from homeassistant.components.bond.const import (
|
||||||
DOMAIN as BOND_DOMAIN,
|
DOMAIN as BOND_DOMAIN,
|
||||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.bond.fan import PRESET_MODE_BREEZE
|
||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
ATTR_DIRECTION,
|
ATTR_DIRECTION,
|
||||||
|
ATTR_PRESET_MODE,
|
||||||
|
ATTR_PRESET_MODES,
|
||||||
ATTR_SPEED,
|
ATTR_SPEED,
|
||||||
ATTR_SPEED_LIST,
|
ATTR_SPEED_LIST,
|
||||||
DIRECTION_FORWARD,
|
DIRECTION_FORWARD,
|
||||||
DIRECTION_REVERSE,
|
DIRECTION_REVERSE,
|
||||||
DOMAIN as FAN_DOMAIN,
|
DOMAIN as FAN_DOMAIN,
|
||||||
SERVICE_SET_DIRECTION,
|
SERVICE_SET_DIRECTION,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
SERVICE_SET_SPEED,
|
SERVICE_SET_SPEED,
|
||||||
SPEED_OFF,
|
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(
|
async def turn_fan_on(
|
||||||
hass: core.HomeAssistant,
|
hass: core.HomeAssistant,
|
||||||
fan_id: str,
|
fan_id: str,
|
||||||
speed: str | None = None,
|
speed: str | None = None,
|
||||||
percentage: int | None = None,
|
percentage: int | None = None,
|
||||||
|
preset_mode: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Turn the fan on at the specified speed."""
|
"""Turn the fan on at the specified speed."""
|
||||||
service_data = {ATTR_ENTITY_ID: fan_id}
|
service_data = {ATTR_ENTITY_ID: fan_id}
|
||||||
|
if preset_mode:
|
||||||
|
service_data[fan.ATTR_PRESET_MODE] = preset_mode
|
||||||
if speed:
|
if speed:
|
||||||
service_data[fan.ATTR_SPEED] = speed
|
service_data[fan.ATTR_SPEED] = speed
|
||||||
if percentage:
|
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))
|
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):
|
async def test_turn_on_fan_without_speed(hass: core.HomeAssistant):
|
||||||
"""Tests that turn on command delegates to turn on API."""
|
"""Tests that turn on command delegates to turn on API."""
|
||||||
await setup_platform(
|
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):
|
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(
|
await setup_platform(
|
||||||
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
|
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue