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 bond cover assumed state and local polling #37666

Merged
merged 4 commits into from
Jul 9, 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
21 changes: 15 additions & 6 deletions homeassistant/components/bond/cover.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Support for Bond covers."""
import logging
from typing import Any, Callable, Dict, List, Optional

from bond import BOND_DEVICE_TYPE_MOTORIZED_SHADES, Bond
Expand All @@ -13,13 +12,11 @@
from .const import DOMAIN
from .utils import BondDevice, get_bond_devices

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: Callable[[List[Entity]], None],
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up Bond cover devices."""
bond: Bond = hass.data[DOMAIN][entry.entry_id]
Expand All @@ -32,7 +29,7 @@ async def async_setup_entry(
if device.type == BOND_DEVICE_TYPE_MOTORIZED_SHADES
]

async_add_entities(covers)
async_add_entities(covers, True)


class BondCover(CoverEntity):
Expand All @@ -42,6 +39,7 @@ def __init__(self, bond: Bond, device: BondDevice):
"""Create HA entity representing Bond cover."""
self._bond = bond
self._device = device
self._closed: Optional[bool] = None

@property
def device_class(self) -> Optional[str]:
Expand All @@ -63,10 +61,21 @@ def device_info(self) -> Optional[Dict[str, Any]]:
"""Get a an HA device representing this cover."""
return {ATTR_NAME: self.name, "identifiers": {(DOMAIN, self._device.device_id)}}

@property
def assumed_state(self) -> bool:
"""Let HA know this entity relies on an assumed state tracked by Bond."""
return True

def update(self):
"""Fetch assumed state of the cover from the hub using API."""
state: dict = self._bond.getDeviceState(self._device.device_id)
cover_open = state.get("open")
self._closed = True if cover_open == 0 else False if cover_open == 1 else None

@property
def is_closed(self):
"""Return if the cover is closed or not."""
return None
return self._closed

def open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
Expand Down
80 changes: 71 additions & 9 deletions tests/components/bond/test_cover.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Tests for the Bond cover device."""
from datetime import timedelta
import logging

from bond import BOND_DEVICE_TYPE_MOTORIZED_SHADES

from homeassistant import core
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
Expand All @@ -11,35 +13,45 @@
SERVICE_STOP_COVER,
)
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__)

TEST_DEVICE_IDS = ["device-1"]
TEST_DEVICE = {"name": "name-1", "type": BOND_DEVICE_TYPE_MOTORIZED_SHADES}
TEST_COVER_DEVICE = {"name": "name-1", "type": BOND_DEVICE_TYPE_MOTORIZED_SHADES}


async def test_entity_registry(hass):
async def test_entity_registry(hass: core.HomeAssistant):
"""Tests that the devices are registered in the entity registry."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch("homeassistant.components.bond.Bond.getDevice", return_value=TEST_DEVICE):
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

registry: EntityRegistry = await hass.helpers.entity_registry.async_get_registry()
assert [key for key in registry.entities.keys()] == ["cover.name_1"]


async def test_open_cover(hass):
async def test_open_cover(hass: core.HomeAssistant):
"""Tests that open cover command delegates to API."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch("homeassistant.components.bond.Bond.getDevice", return_value=TEST_DEVICE):
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

with patch("homeassistant.components.bond.Bond.open") as mock_open:
Expand All @@ -53,12 +65,16 @@ async def test_open_cover(hass):
mock_open.assert_called_once()


async def test_close_cover(hass):
async def test_close_cover(hass: core.HomeAssistant):
"""Tests that close cover command delegates to API."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch("homeassistant.components.bond.Bond.getDevice", return_value=TEST_DEVICE):
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

with patch("homeassistant.components.bond.Bond.close") as mock_close:
Expand All @@ -72,12 +88,16 @@ async def test_close_cover(hass):
mock_close.assert_called_once()


async def test_stop_cover(hass):
async def test_stop_cover(hass: core.HomeAssistant):
"""Tests that stop cover command delegates to API."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch("homeassistant.components.bond.Bond.getDevice", return_value=TEST_DEVICE):
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

with patch("homeassistant.components.bond.Bond.hold") as mock_hold:
Expand All @@ -89,3 +109,45 @@ async def test_stop_cover(hass):
)
await hass.async_block_till_done()
mock_hold.assert_called_once()


async def test_update_reports_open_cover(hass: core.HomeAssistant):
"""Tests that update command sets correct state when Bond API reports cover is open."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

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

assert hass.states.get("cover.name_1").state == "open"


async def test_update_reports_closed_cover(hass: core.HomeAssistant):
"""Tests that update command sets correct state when Bond API reports cover is closed."""

with patch(
"homeassistant.components.bond.Bond.getDeviceIds", return_value=TEST_DEVICE_IDS
), patch(
"homeassistant.components.bond.Bond.getDevice", return_value=TEST_COVER_DEVICE
), patch(
"homeassistant.components.bond.Bond.getDeviceState", return_value={}
):
await setup_platform(hass, COVER_DOMAIN)

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

assert hass.states.get("cover.name_1").state == "closed"