Skip to content

Commit

Permalink
Add support for fan direction in bond integration (home-assistant#37789)
Browse files Browse the repository at this point in the history
* Add support for fan direction in bond integration

* Add support for fan direction (PR feedback)
  • Loading branch information
prystupa authored Jul 12, 2020
1 parent 5384448 commit e9440c4
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 9 deletions.
30 changes: 28 additions & 2 deletions homeassistant/components/bond/fan.py
Original file line number Diff line number Diff line change
@@ -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,
)
Expand Down Expand Up @@ -48,13 +51,17 @@ def __init__(self, hub: BondHub, device: BondDevice):

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
Expand All @@ -72,11 +79,23 @@ def speed_list(self) -> list:
"""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."""
Expand All @@ -92,3 +111,10 @@ def turn_on(self, speed: Optional[str] = None, **kwargs) -> None:
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)
22 changes: 18 additions & 4 deletions homeassistant/components/bond/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import List, Optional

from bond import Bond
from bond import Actions, Bond


class BondDevice:
Expand All @@ -23,10 +23,24 @@ def type(self) -> str:
"""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:
Expand Down
55 changes: 52 additions & 3 deletions tests/components/bond/test_fan.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,7 +27,7 @@ def ceiling_fan(name: str):
return {
"name": name,
"type": DeviceTypes.CEILING_FAN,
"actions": ["SetSpeed"],
"actions": ["SetSpeed", "SetDirection"],
}


Expand Down Expand Up @@ -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()

0 comments on commit e9440c4

Please sign in to comment.