diff --git a/homeassistant/components/bond/__init__.py b/homeassistant/components/bond/__init__.py index 39db50bf9c1..013b061c08e 100644 --- a/homeassistant/components/bond/__init__.py +++ b/homeassistant/components/bond/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import device_registry as dr from .const import DOMAIN from .utils import BondHub -PLATFORMS = ["cover", "fan", "light"] +PLATFORMS = ["cover", "fan", "light", "switch"] async def async_setup(hass: HomeAssistant, config: dict): diff --git a/homeassistant/components/bond/switch.py b/homeassistant/components/bond/switch.py new file mode 100644 index 00000000000..e7892272bbf --- /dev/null +++ b/homeassistant/components/bond/switch.py @@ -0,0 +1,60 @@ +"""Support for Bond generic devices.""" +from typing import Any, Callable, List, Optional + +from bond import DeviceTypes + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity + +from ..switch import SwitchEntity +from .const import DOMAIN +from .entity import BondEntity +from .utils import BondDevice, BondHub + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: Callable[[List[Entity], bool], None], +) -> None: + """Set up Bond generic devices.""" + hub: BondHub = hass.data[DOMAIN][entry.entry_id] + + devices = await hass.async_add_executor_job(hub.get_bond_devices) + + switches = [ + BondSwitch(hub, device) + for device in devices + if device.type == DeviceTypes.GENERIC_DEVICE + ] + + async_add_entities(switches, True) + + +class BondSwitch(BondEntity, SwitchEntity): + """Representation of a Bond generic device.""" + + def __init__(self, hub: BondHub, device: BondDevice): + """Create HA entity representing Bond generic device (switch).""" + super().__init__(hub, device) + + self._power: Optional[bool] = None + + @property + def is_on(self) -> bool: + """Return True if power is on.""" + return self._power == 1 + + def turn_on(self, **kwargs: Any) -> None: + """Turn the device on.""" + self._hub.bond.turnOn(self._device.device_id) + + def turn_off(self, **kwargs: Any) -> None: + """Turn the device off.""" + self._hub.bond.turnOff(self._device.device_id) + + def update(self): + """Fetch assumed state of the device from the hub using API.""" + state: dict = self._hub.bond.getDeviceState(self._device.device_id) + self._power = state.get("power") diff --git a/tests/components/bond/test_init.py b/tests/components/bond/test_init.py index bec002b38a8..23c11199879 100644 --- a/tests/components/bond/test_init.py +++ b/tests/components/bond/test_init.py @@ -31,7 +31,9 @@ async def test_async_setup_entry_sets_up_hub_and_supported_domains(hass: HomeAss "homeassistant.components.bond.fan.async_setup_entry" ) as mock_fan_async_setup_entry, patch( "homeassistant.components.bond.light.async_setup_entry" - ) as mock_light_async_setup_entry: + ) as mock_light_async_setup_entry, patch( + "homeassistant.components.bond.switch.async_setup_entry" + ) as mock_switch_async_setup_entry: result = await setup_bond_entity( hass, config_entry, @@ -61,6 +63,7 @@ async def test_async_setup_entry_sets_up_hub_and_supported_domains(hass: HomeAss 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 + assert len(mock_switch_async_setup_entry.mock_calls) == 1 async def test_unload_config_entry(hass: HomeAssistant): diff --git a/tests/components/bond/test_switch.py b/tests/components/bond/test_switch.py new file mode 100644 index 00000000000..121bb505cdb --- /dev/null +++ b/tests/components/bond/test_switch.py @@ -0,0 +1,87 @@ +"""Tests for the Bond switch device.""" +from datetime import timedelta +import logging + +from bond import DeviceTypes + +from homeassistant import core +from homeassistant.components.switch import DOMAIN as SWITCH_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 generic_device(name: str): + """Create a generic device with given name.""" + return {"name": name, "type": DeviceTypes.GENERIC_DEVICE} + + +async def test_entity_registry(hass: core.HomeAssistant): + """Tests that the devices are registered in the entity registry.""" + await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) + + registry: EntityRegistry = await hass.helpers.entity_registry.async_get_registry() + assert [key for key in registry.entities] == ["switch.name_1"] + + +async def test_turn_on_switch(hass: core.HomeAssistant): + """Tests that turn on command delegates to API.""" + await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) + + with patch("homeassistant.components.bond.Bond.turnOn") as mock_turn_on: + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.name_1"}, + blocking=True, + ) + await hass.async_block_till_done() + mock_turn_on.assert_called_once() + + +async def test_turn_off_switch(hass: core.HomeAssistant): + """Tests that turn off command delegates to API.""" + await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) + + with patch("homeassistant.components.bond.Bond.turnOff") as mock_turn_off: + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.name_1"}, + blocking=True, + ) + await hass.async_block_till_done() + mock_turn_off.assert_called_once() + + +async def test_update_reports_switch_is_on(hass: core.HomeAssistant): + """Tests that update command sets correct state when Bond API reports the device is on.""" + await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) + + with patch( + "homeassistant.components.bond.Bond.getDeviceState", return_value={"power": 1} + ): + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + assert hass.states.get("switch.name_1").state == "on" + + +async def test_update_reports_switch_is_off(hass: core.HomeAssistant): + """Tests that update command sets correct state when Bond API reports the device is off.""" + await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) + + with patch( + "homeassistant.components.bond.Bond.getDeviceState", return_value={"power": 0} + ): + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + assert hass.states.get("switch.name_1").state == "off"