From 85e463d507cd7de5df391863de5c479aaa6719fb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 28 Jan 2021 03:23:12 -0600 Subject: [PATCH] Update bond to use new fan entity model (#45534) --- homeassistant/components/bond/fan.py | 69 +++++++++++----------------- tests/components/bond/test_fan.py | 63 +++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index 6e33aa6d161..18eeb912ed8 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -1,17 +1,13 @@ """Support for Bond fans.""" import logging import math -from typing import Any, Callable, List, Optional +from typing import Any, Callable, List, Optional, Tuple from bond_api import Action, DeviceType, Direction from homeassistant.components.fan import ( DIRECTION_FORWARD, DIRECTION_REVERSE, - SPEED_HIGH, - SPEED_LOW, - SPEED_MEDIUM, - SPEED_OFF, SUPPORT_DIRECTION, SUPPORT_SET_SPEED, FanEntity, @@ -19,6 +15,10 @@ from homeassistant.components.fan import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity +from homeassistant.util.percentage import ( + percentage_to_ranged_value, + ranged_value_to_percentage, +) from .const import DOMAIN from .entity import BondEntity @@ -70,22 +70,16 @@ class BondFan(BondEntity, FanEntity): return features @property - def speed(self) -> Optional[str]: - """Return the current speed.""" - if self._power == 0: - return SPEED_OFF - if not self._power or not self._speed: - return None - - # map 1..max_speed Bond speed to 1..3 HA speed - max_speed = max(self._device.props.get("max_speed", 3), self._speed) - ha_speed = math.ceil(self._speed * (len(self.speed_list) - 1) / max_speed) - return self.speed_list[ha_speed] + def _speed_range(self) -> Tuple[int, int]: + """Return the range of speeds.""" + return (1, self._device.props.get("max_speed", 3)) @property - def speed_list(self) -> list: - """Get the list of available speeds.""" - return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] + def percentage(self) -> Optional[str]: + """Return the current speed percentage for the fan.""" + if not self._speed or not self._power: + return 0 + return ranged_value_to_percentage(self._speed_range, self._speed) @property def current_direction(self) -> Optional[str]: @@ -98,33 +92,27 @@ class BondFan(BondEntity, FanEntity): return direction - async def async_set_speed(self, speed: str) -> None: + async def async_set_percentage(self, percentage: int) -> None: """Set the desired speed for the fan.""" - _LOGGER.debug("async_set_speed called with speed %s", speed) + _LOGGER.debug("async_set_percentage called with percentage %s", percentage) - if speed == SPEED_OFF: + if percentage == 0: await self.async_turn_off() return - max_speed = self._device.props.get("max_speed", 3) - if speed == SPEED_LOW: - bond_speed = 1 - elif speed == SPEED_HIGH: - bond_speed = max_speed - else: - bond_speed = math.ceil(max_speed / 2) + bond_speed = math.ceil( + percentage_to_ranged_value(self._speed_range, percentage) + ) + _LOGGER.debug( + "async_set_percentage converted percentage %s to bond speed %s", + percentage, + bond_speed, + ) await self._hub.bond.action( self._device.device_id, Action.set_speed(bond_speed) ) - # - # The fan entity model has changed to use percentages and preset_modes - # instead of speeds. - # - # Please review - # https://developers.home-assistant.io/docs/core/entity/fan/ - # async def async_turn_on( self, speed: Optional[str] = None, @@ -133,13 +121,10 @@ class BondFan(BondEntity, FanEntity): **kwargs, ) -> None: """Turn on the fan.""" - _LOGGER.debug("Fan async_turn_on called with speed %s", speed) + _LOGGER.debug("Fan async_turn_on called with percentage %s", percentage) - if speed is not None: - if speed == SPEED_OFF: - await self.async_turn_off() - else: - await self.async_set_speed(speed) + if percentage is not None: + await self.async_set_percentage(percentage) else: await self._hub.bond.action(self._device.device_id, Action.turn_on()) diff --git a/tests/components/bond/test_fan.py b/tests/components/bond/test_fan.py index 1adea282a30..49a6e4a5b68 100644 --- a/tests/components/bond/test_fan.py +++ b/tests/components/bond/test_fan.py @@ -41,12 +41,17 @@ def ceiling_fan(name: str): async def turn_fan_on( - hass: core.HomeAssistant, fan_id: str, speed: Optional[str] = None + hass: core.HomeAssistant, + fan_id: str, + speed: Optional[str] = None, + percentage: Optional[int] = None, ) -> None: """Turn the fan on at the specified speed.""" service_data = {ATTR_ENTITY_ID: fan_id} if speed: service_data[fan.ATTR_SPEED] = speed + if percentage: + service_data[fan.ATTR_PERCENTAGE] = percentage await hass.services.async_call( FAN_DOMAIN, SERVICE_TURN_ON, @@ -93,13 +98,13 @@ async def test_non_standard_speed_list(hass: core.HomeAssistant): with patch_bond_action() as mock_set_speed_low: await turn_fan_on(hass, "fan.name_1", fan.SPEED_LOW) mock_set_speed_low.assert_called_once_with( - "test-device-id", Action.set_speed(1) + "test-device-id", Action.set_speed(2) ) with patch_bond_action() as mock_set_speed_medium: await turn_fan_on(hass, "fan.name_1", fan.SPEED_MEDIUM) mock_set_speed_medium.assert_called_once_with( - "test-device-id", Action.set_speed(3) + "test-device-id", Action.set_speed(4) ) with patch_bond_action() as mock_set_speed_high: @@ -135,6 +140,58 @@ async def test_turn_on_fan_with_speed(hass: core.HomeAssistant): mock_set_speed.assert_called_with("test-device-id", Action.set_speed(1)) +async def test_turn_on_fan_with_percentage_3_speeds(hass: core.HomeAssistant): + """Tests that turn on command delegates to set speed API.""" + await setup_platform( + hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id" + ) + + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=10) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(1)) + + mock_set_speed.reset_mock() + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=50) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(2)) + + mock_set_speed.reset_mock() + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=100) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(3)) + + +async def test_turn_on_fan_with_percentage_6_speeds(hass: core.HomeAssistant): + """Tests that turn on command delegates to set speed API.""" + await setup_platform( + hass, + FAN_DOMAIN, + ceiling_fan("name-1"), + bond_device_id="test-device-id", + props={"max_speed": 6}, + ) + + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=10) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(1)) + + mock_set_speed.reset_mock() + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=50) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(3)) + + mock_set_speed.reset_mock() + with patch_bond_action() as mock_set_speed, patch_bond_device_state(): + await turn_fan_on(hass, "fan.name_1", percentage=100) + + mock_set_speed.assert_called_with("test-device-id", Action.set_speed(6)) + + async def test_turn_on_fan_without_speed(hass: core.HomeAssistant): """Tests that turn on command delegates to turn on API.""" await setup_platform(