diff --git a/homeassistant/components/bond/light.py b/homeassistant/components/bond/light.py index 77afb188111..574c50dc3e3 100644 --- a/homeassistant/components/bond/light.py +++ b/homeassistant/components/bond/light.py @@ -47,20 +47,45 @@ class BondLight(BondEntity, LightEntity): def __init__(self, hub: BondHub, device: BondDevice): """Create HA entity representing Bond fan.""" super().__init__(hub, device) - + self._brightness: Optional[int] = None self._light: Optional[int] = None def _apply_state(self, state: dict): self._light = state.get("light") + self._brightness = state.get("brightness") + + @property + def supported_features(self) -> Optional[int]: + """Flag supported features.""" + features = 0 + if self._device.supports_set_brightness(): + features |= SUPPORT_BRIGHTNESS + + return features @property def is_on(self) -> bool: """Return if light is currently on.""" return self._light == 1 + @property + def brightness(self) -> int: + """Return the brightness of this light between 1..255.""" + brightness_value = ( + round(self._brightness * 255 / 100) if self._brightness else None + ) + return brightness_value + async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" - await self._hub.bond.action(self._device.device_id, Action.turn_light_on()) + brightness = kwargs.get(ATTR_BRIGHTNESS) + if brightness: + await self._hub.bond.action( + self._device.device_id, + Action(Action.SET_BRIGHTNESS, round((brightness * 100) / 255)), + ) + else: + await self._hub.bond.action(self._device.device_id, Action.turn_light_on()) async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the light.""" diff --git a/homeassistant/components/bond/manifest.json b/homeassistant/components/bond/manifest.json index 9d5a9975503..3b3be1fb461 100644 --- a/homeassistant/components/bond/manifest.json +++ b/homeassistant/components/bond/manifest.json @@ -3,10 +3,6 @@ "name": "Bond", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bond", - "requirements": [ - "bond-api==0.1.7" - ], - "codeowners": [ - "@prystupa" - ] + "requirements": ["bond-api==0.1.7"], + "codeowners": ["@prystupa"] } diff --git a/homeassistant/components/bond/utils.py b/homeassistant/components/bond/utils.py index 416d5c8eb32..b45ca9bd251 100644 --- a/homeassistant/components/bond/utils.py +++ b/homeassistant/components/bond/utils.py @@ -60,6 +60,11 @@ class BondDevice: ] ) + def supports_set_brightness(self) -> bool: + """Return True if this device supports setting a light brightness.""" + actions: List[str] = self._attrs["actions"] + return bool([action for action in actions if action in [Action.SET_BRIGHTNESS]]) + class BondHub: """Hub device representing Bond Bridge.""" diff --git a/tests/components/bond/test_light.py b/tests/components/bond/test_light.py index 555da5e707f..e1167eac107 100644 --- a/tests/components/bond/test_light.py +++ b/tests/components/bond/test_light.py @@ -5,10 +5,15 @@ import logging from bond_api import Action, DeviceType from homeassistant import core -from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN as LIGHT_DOMAIN +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + DOMAIN as LIGHT_DOMAIN, + SUPPORT_BRIGHTNESS, +) from homeassistant.const import ( ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, + ATTR_SUPPORTED_FEATURES, SERVICE_TURN_OFF, SERVICE_TURN_ON, ) @@ -36,6 +41,15 @@ def ceiling_fan(name: str): } +def dimmable_ceiling_fan(name: str): + """Create a ceiling fan (that has built-in light) with given name.""" + return { + "name": name, + "type": DeviceType.CEILING_FAN, + "actions": [Action.TURN_LIGHT_ON, Action.TURN_LIGHT_OFF, Action.SET_BRIGHTNESS], + } + + def fireplace(name: str): """Create a fireplace with given name.""" return {"name": name, "type": DeviceType.FIREPLACE} @@ -128,6 +142,52 @@ async def test_turn_off_light(hass: core.HomeAssistant): ) +async def test_brightness_support(hass: core.HomeAssistant): + """Tests that a dimmable light should support the brightness feature.""" + await setup_platform( + hass, + LIGHT_DOMAIN, + dimmable_ceiling_fan("name-1"), + bond_device_id="test-device-id", + ) + + state = hass.states.get("light.name_1") + assert state.attributes[ATTR_SUPPORTED_FEATURES] & SUPPORT_BRIGHTNESS + + +async def test_brightness_not_supported(hass: core.HomeAssistant): + """Tests that a non-dimmable light should not support the brightness feature.""" + await setup_platform( + hass, LIGHT_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id", + ) + + state = hass.states.get("light.name_1") + assert not state.attributes[ATTR_SUPPORTED_FEATURES] & SUPPORT_BRIGHTNESS + + +async def test_turn_on_light_with_brightness(hass: core.HomeAssistant): + """Tests that turn on command, on a dimmable light, delegates to API and parses brightness.""" + await setup_platform( + hass, + LIGHT_DOMAIN, + dimmable_ceiling_fan("name-1"), + bond_device_id="test-device-id", + ) + + with patch_bond_action() as mock_set_brightness, patch_bond_device_state(): + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 128}, + blocking=True, + ) + await hass.async_block_till_done() + + mock_set_brightness.assert_called_once_with( + "test-device-id", Action(Action.SET_BRIGHTNESS, 50) + ) + + async def test_update_reports_light_is_on(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports the light is on.""" await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1")) @@ -220,3 +280,14 @@ async def test_light_available(hass: core.HomeAssistant): await help_test_entity_available( hass, LIGHT_DOMAIN, ceiling_fan("name-1"), "light.name_1" ) + + +async def test_parse_brightness(hass: core.HomeAssistant): + """Tests that reported brightness level (0..100) converted to HA brightness (0...255).""" + await setup_platform(hass, LIGHT_DOMAIN, dimmable_ceiling_fan("name-1")) + + with patch_bond_device_state(return_value={"light": 1, "brightness": 50}): + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + assert hass.states.get("light.name_1").attributes[ATTR_BRIGHTNESS] == 128