Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for lights in bond integration #37802

Merged
merged 1 commit into from
Jul 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add basic support for lights in bond integration
  • Loading branch information
prystupa committed Jul 12, 2020
commit 2d00a15c864d792143a770e5ef430c41c117c48c
2 changes: 1 addition & 1 deletion homeassistant/components/bond/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .const import DOMAIN
from .utils import BondHub

PLATFORMS = ["cover", "fan"]
PLATFORMS = ["cover", "fan", "light"]


async def async_setup(hass: HomeAssistant, config: dict):
Expand Down
61 changes: 61 additions & 0 deletions homeassistant/components/bond/light.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Support for Bond lights."""
from typing import Any, Callable, List, Optional

from bond import DeviceTypes

from homeassistant.components.light import LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity

from . import BondHub
from .const import DOMAIN
from .entity import BondEntity
from .utils import BondDevice


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up Bond light devices."""
hub: BondHub = hass.data[DOMAIN][entry.entry_id]

devices = await hass.async_add_executor_job(hub.get_bond_devices)

lights = [
BondLight(hub, device)
for device in devices
if device.type == DeviceTypes.CEILING_FAN and device.supports_light()
]

async_add_entities(lights, True)


class BondLight(BondEntity, LightEntity):
"""Representation of a Bond light."""

def __init__(self, hub: BondHub, device: BondDevice):
"""Create HA entity representing Bond fan."""
super().__init__(hub, device)

self._light: Optional[int] = None

@property
def is_on(self) -> bool:
"""Return if light is currently on."""
return self._light == 1

def update(self):
"""Fetch assumed state of the light from the hub using API."""
state: dict = self._hub.bond.getDeviceState(self._device.device_id)
self._light = state.get("light")

def turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
self._hub.bond.turnLightOn(self._device.device_id)

def turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
self._hub.bond.turnLightOff(self._device.device_id)
14 changes: 14 additions & 0 deletions homeassistant/components/bond/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ def supports_direction(self) -> bool:
> 0
)

def supports_light(self) -> bool:
"""Return True if this device supports any of the light related commands."""
actions: List[str] = self._attrs["actions"]
return (
len(
prystupa marked this conversation as resolved.
Show resolved Hide resolved
[
action
for action in actions
if action in [Actions.TURN_LIGHT_ON, Actions.TOGGLE_LIGHT]
]
)
> 0
)


class BondHub:
"""Hub device representing Bond Bridge."""
Expand Down
5 changes: 4 additions & 1 deletion tests/components/bond/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ async def test_async_setup_entry_sets_up_hub_and_supported_domains(hass: HomeAss
"homeassistant.components.bond.cover.async_setup_entry"
) as mock_cover_async_setup_entry, patch(
"homeassistant.components.bond.fan.async_setup_entry"
) as mock_fan_async_setup_entry:
) as mock_fan_async_setup_entry, patch(
"homeassistant.components.bond.light.async_setup_entry"
) as mock_light_async_setup_entry:
result = await setup_bond_entity(
hass,
config_entry,
Expand Down Expand Up @@ -58,6 +60,7 @@ async def test_async_setup_entry_sets_up_hub_and_supported_domains(hass: HomeAss
# verify supported domains are setup
assert len(mock_cover_async_setup_entry.mock_calls) == 1
assert len(mock_fan_async_setup_entry.mock_calls) == 1
assert len(mock_light_async_setup_entry.mock_calls) == 1


async def test_unload_config_entry(hass: HomeAssistant):
Expand Down
93 changes: 93 additions & 0 deletions tests/components/bond/test_light.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Tests for the Bond light device."""
from datetime import timedelta
import logging

from bond import Actions, DeviceTypes

from homeassistant import core
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
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

from .common import setup_platform

from tests.async_mock import patch
from tests.common import async_fire_time_changed

_LOGGER = logging.getLogger(__name__)


def ceiling_fan(name: str):
"""Create a ceiling fan (that has built-in light) with given name."""
return {
"name": name,
"type": DeviceTypes.CEILING_FAN,
"actions": [Actions.TOGGLE_LIGHT],
}


async def test_entity_registry(hass: core.HomeAssistant):
"""Tests that the devices are registered in the entity registry."""
await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1"))

registry: EntityRegistry = await hass.helpers.entity_registry.async_get_registry()
assert [key for key in registry.entities.keys()] == ["light.name_1"]
prystupa marked this conversation as resolved.
Show resolved Hide resolved


async def test_turn_on_light(hass: core.HomeAssistant):
"""Tests that turn on command delegates to API."""
await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1"))

with patch("homeassistant.components.bond.Bond.turnLightOn") as mock_turn_light_on:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "light.name_1"},
blocking=True,
)
await hass.async_block_till_done()
mock_turn_light_on.assert_called_once()


async def test_turn_off_light(hass: core.HomeAssistant):
"""Tests that turn off command delegates to API."""
await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1"))

with patch(
"homeassistant.components.bond.Bond.turnLightOff"
) as mock_turn_light_off:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.name_1"},
blocking=True,
)
await hass.async_block_till_done()
mock_turn_light_off.assert_called_once()


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"))

with patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={"light": 1}
):
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
await hass.async_block_till_done()

assert hass.states.get("light.name_1").state == "on"


async def test_update_reports_light_is_off(hass: core.HomeAssistant):
"""Tests that update command sets correct state when Bond API reports the light is off."""
await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1"))

with patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={"light": 0}
):
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
await hass.async_block_till_done()

assert hass.states.get("light.name_1").state == "off"