Add support for fan direction in bond integration (#37789)

* Add support for fan direction in bond integration

* Add support for fan direction (PR feedback)
pull/37795/head
Eugene Prystupa 2020-07-12 16:30:24 -04:00 committed by GitHub
parent 53844488d8
commit e9440c49d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 9 deletions

View File

@ -1,13 +1,16 @@
"""Support for Bond fans."""
from typing import Any, Callable, List, Optional
from bond import DeviceTypes
from bond import DeviceTypes, Directions
from homeassistant.components.fan import (
DIRECTION_FORWARD,
DIRECTION_REVERSE,
SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM,
SPEED_OFF,
SUPPORT_DIRECTION,
SUPPORT_SET_SPEED,
FanEntity,
)
@ -48,13 +51,17 @@ class BondFan(BondEntity, FanEntity):
self._power: Optional[bool] = None
self._speed: Optional[int] = None
self._direction: Optional[int] = None
@property
def supported_features(self) -> int:
"""Flag supported features."""
features = 0
if self._device.supports_command("SetSpeed"):
if self._device.supports_speed():
features |= SUPPORT_SET_SPEED
if self._device.supports_direction():
features |= SUPPORT_DIRECTION
return features
@property
@ -72,11 +79,23 @@ class BondFan(BondEntity, FanEntity):
"""Get the list of available speeds."""
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
@property
def current_direction(self) -> Optional[str]:
"""Return fan rotation direction."""
direction = None
if self._direction == Directions.FORWARD:
direction = DIRECTION_FORWARD
elif self._direction == Directions.REVERSE:
direction = DIRECTION_REVERSE
return direction
def update(self):
"""Fetch assumed state of the fan from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._power = state.get("power")
self._speed = state.get("speed")
self._direction = state.get("direction")
def set_speed(self, speed: str) -> None:
"""Set the desired speed for the fan."""
@ -92,3 +111,10 @@ class BondFan(BondEntity, FanEntity):
def turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off."""
self._hub.bond.turnOff(self._device.device_id)
def set_direction(self, direction: str) -> None:
"""Set fan rotation direction."""
bond_direction = (
Directions.REVERSE if direction == DIRECTION_REVERSE else Directions.FORWARD
)
self._hub.bond.setDirection(self._device.device_id, bond_direction)

View File

@ -2,7 +2,7 @@
from typing import List, Optional
from bond import Bond
from bond import Actions, Bond
class BondDevice:
@ -23,10 +23,24 @@ class BondDevice:
"""Get the type of this device."""
return self._attrs["type"]
def supports_command(self, command: str) -> bool:
"""Return True if this device supports specified command."""
def supports_speed(self) -> bool:
"""Return True if this device supports any of the speed related commands."""
actions: List[str] = self._attrs["actions"]
return command in actions
return len([action for action in actions if action in [Actions.SET_SPEED]]) > 0
def supports_direction(self) -> bool:
"""Return True if this device supports any of the direction related commands."""
actions: List[str] = self._attrs["actions"]
return (
len(
[
action
for action in actions
if action in [Actions.SET_DIRECTION, Actions.TOGGLE_DIRECTION]
]
)
> 0
)
class BondHub:

View File

@ -1,11 +1,17 @@
"""Tests for the Bond fan device."""
from datetime import timedelta
from bond import DeviceTypes
from bond import DeviceTypes, Directions
from homeassistant import core
from homeassistant.components import fan
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
from homeassistant.components.fan import (
ATTR_DIRECTION,
DIRECTION_FORWARD,
DIRECTION_REVERSE,
DOMAIN as FAN_DOMAIN,
SERVICE_SET_DIRECTION,
)
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import utcnow
@ -21,7 +27,7 @@ def ceiling_fan(name: str):
return {
"name": name,
"type": DeviceTypes.CEILING_FAN,
"actions": ["SetSpeed"],
"actions": ["SetSpeed", "SetDirection"],
}
@ -90,3 +96,46 @@ async def test_update_reports_fan_off(hass: core.HomeAssistant):
await hass.async_block_till_done()
assert hass.states.get("fan.name_1").state == "off"
async def test_update_reports_direction_forward(hass: core.HomeAssistant):
"""Tests that update command sets correct direction when Bond API reports fan direction is forward."""
await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1"))
with patch(
"homeassistant.components.bond.Bond.getDeviceState",
return_value={"direction": Directions.FORWARD},
):
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
await hass.async_block_till_done()
assert hass.states.get("fan.name_1").attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
async def test_update_reports_direction_reverse(hass: core.HomeAssistant):
"""Tests that update command sets correct direction when Bond API reports fan direction is reverse."""
await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1"))
with patch(
"homeassistant.components.bond.Bond.getDeviceState",
return_value={"direction": Directions.REVERSE},
):
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
await hass.async_block_till_done()
assert hass.states.get("fan.name_1").attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
async def test_set_fan_direction(hass: core.HomeAssistant):
"""Tests that set direction command delegates to API."""
await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1"))
with patch("homeassistant.components.bond.Bond.setDirection") as mock_set_direction:
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_DIRECTION,
{ATTR_ENTITY_ID: "fan.name_1", ATTR_DIRECTION: DIRECTION_FORWARD},
blocking=True,
)
await hass.async_block_till_done()
mock_set_direction.assert_called_once()