Add support for dimmable bond lights (#38203)

* Add support for dimmable lights

* Fix formatting

* Add supported features test on Bond Light

* Add more tests to bond light and fixes comments

* Fix rebase conflict resolution

* Apply suggestions from code review

Co-authored-by: Chris Talkington <chris@talkingtontech.com>
This commit is contained in:
Marcio Granzotto Rodrigues 2020-07-29 22:01:59 -03:00 committed by GitHub
parent fa9866db96
commit 8ab1b41974
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 9 deletions

View File

@ -47,19 +47,44 @@ class BondLight(BondEntity, LightEntity):
def __init__(self, hub: BondHub, device: BondDevice): def __init__(self, hub: BondHub, device: BondDevice):
"""Create HA entity representing Bond fan.""" """Create HA entity representing Bond fan."""
super().__init__(hub, device) super().__init__(hub, device)
self._brightness: Optional[int] = None
self._light: Optional[int] = None self._light: Optional[int] = None
def _apply_state(self, state: dict): def _apply_state(self, state: dict):
self._light = state.get("light") 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 @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return if light is currently on.""" """Return if light is currently on."""
return self._light == 1 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: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light.""" """Turn on the light."""
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()) await self._hub.bond.action(self._device.device_id, Action.turn_light_on())
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:

View File

@ -3,10 +3,6 @@
"name": "Bond", "name": "Bond",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bond", "documentation": "https://www.home-assistant.io/integrations/bond",
"requirements": [ "requirements": ["bond-api==0.1.7"],
"bond-api==0.1.7" "codeowners": ["@prystupa"]
],
"codeowners": [
"@prystupa"
]
} }

View File

@ -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: class BondHub:
"""Hub device representing Bond Bridge.""" """Hub device representing Bond Bridge."""

View File

@ -5,10 +5,15 @@ import logging
from bond_api import Action, DeviceType from bond_api import Action, DeviceType
from homeassistant import core 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 ( from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_ASSUMED_STATE,
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, 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): def fireplace(name: str):
"""Create a fireplace with given name.""" """Create a fireplace with given name."""
return {"name": name, "type": DeviceType.FIREPLACE} 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): 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.""" """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")) 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( await help_test_entity_available(
hass, LIGHT_DOMAIN, ceiling_fan("name-1"), "light.name_1" 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