mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Add support for cover positions in bond (#72180)
This commit is contained in:
parent
614c44b9f0
commit
e9c861f2b2
@ -223,6 +223,20 @@ BUTTONS: tuple[BondButtonEntityDescription, ...] = (
|
||||
mutually_exclusive=Action.OPEN,
|
||||
argument=None,
|
||||
),
|
||||
BondButtonEntityDescription(
|
||||
key=Action.INCREASE_POSITION,
|
||||
name="Increase Position",
|
||||
icon="mdi:plus-box",
|
||||
mutually_exclusive=Action.SET_POSITION,
|
||||
argument=STEP_SIZE,
|
||||
),
|
||||
BondButtonEntityDescription(
|
||||
key=Action.DECREASE_POSITION,
|
||||
name="Decrease Position",
|
||||
icon="mdi:minus-box",
|
||||
mutually_exclusive=Action.SET_POSITION,
|
||||
argument=STEP_SIZE,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ from typing import Any
|
||||
from bond_api import Action, BPUPSubscriptions, DeviceType
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
CoverDeviceClass,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
@ -20,6 +21,16 @@ from .entity import BondEntity
|
||||
from .utils import BondDevice, BondHub
|
||||
|
||||
|
||||
def _bond_to_hass_position(bond_position: int) -> int:
|
||||
"""Convert bond 0-open 100-closed to hass 0-closed 100-open."""
|
||||
return abs(bond_position - 100)
|
||||
|
||||
|
||||
def _hass_to_bond_position(hass_position: int) -> int:
|
||||
"""Convert hass 0-closed 100-open to bond 0-open 100-closed."""
|
||||
return 100 - hass_position
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
@ -50,6 +61,8 @@ class BondCover(BondEntity, CoverEntity):
|
||||
"""Create HA entity representing Bond cover."""
|
||||
super().__init__(hub, device, bpup_subs)
|
||||
supported_features = 0
|
||||
if self._device.supports_set_position():
|
||||
supported_features |= CoverEntityFeature.SET_POSITION
|
||||
if self._device.supports_open():
|
||||
supported_features |= CoverEntityFeature.OPEN
|
||||
if self._device.supports_close():
|
||||
@ -67,8 +80,15 @@ class BondCover(BondEntity, CoverEntity):
|
||||
|
||||
def _apply_state(self, state: dict) -> None:
|
||||
cover_open = state.get("open")
|
||||
self._attr_is_closed = (
|
||||
True if cover_open == 0 else False if cover_open == 1 else None
|
||||
self._attr_is_closed = None if cover_open is None else cover_open == 0
|
||||
if (bond_position := state.get("position")) is not None:
|
||||
self._attr_current_cover_position = _bond_to_hass_position(bond_position)
|
||||
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Set the cover position."""
|
||||
await self._hub.bond.action(
|
||||
self._device.device_id,
|
||||
Action.set_position(_hass_to_bond_position(kwargs[ATTR_POSITION])),
|
||||
)
|
||||
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
|
@ -82,6 +82,10 @@ class BondDevice:
|
||||
"""Return True if this device supports any of the direction related commands."""
|
||||
return self._has_any_action({Action.SET_DIRECTION})
|
||||
|
||||
def supports_set_position(self) -> bool:
|
||||
"""Return True if this device supports setting the position."""
|
||||
return self._has_any_action({Action.SET_POSITION})
|
||||
|
||||
def supports_open(self) -> bool:
|
||||
"""Return True if this device supports opening."""
|
||||
return self._has_any_action({Action.OPEN})
|
||||
|
@ -4,15 +4,22 @@ from datetime import timedelta
|
||||
from bond_api import Action, DeviceType
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN, STATE_CLOSED
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_CURRENT_POSITION,
|
||||
ATTR_POSITION,
|
||||
DOMAIN as COVER_DOMAIN,
|
||||
STATE_CLOSED,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_CLOSE_COVER,
|
||||
SERVICE_CLOSE_COVER_TILT,
|
||||
SERVICE_OPEN_COVER,
|
||||
SERVICE_OPEN_COVER_TILT,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
SERVICE_STOP_COVER,
|
||||
SERVICE_STOP_COVER_TILT,
|
||||
STATE_OPEN,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@ -38,6 +45,15 @@ def shades(name: str):
|
||||
}
|
||||
|
||||
|
||||
def shades_with_position(name: str):
|
||||
"""Create motorized shades that supports set position."""
|
||||
return {
|
||||
"name": name,
|
||||
"type": DeviceType.MOTORIZED_SHADES,
|
||||
"actions": [Action.OPEN, Action.CLOSE, Action.HOLD, Action.SET_POSITION],
|
||||
}
|
||||
|
||||
|
||||
def tilt_only_shades(name: str):
|
||||
"""Create motorized shades that only tilt."""
|
||||
return {
|
||||
@ -236,3 +252,64 @@ async def test_cover_available(hass: core.HomeAssistant):
|
||||
await help_test_entity_available(
|
||||
hass, COVER_DOMAIN, shades("name-1"), "cover.name_1"
|
||||
)
|
||||
|
||||
|
||||
async def test_set_position_cover(hass: core.HomeAssistant):
|
||||
"""Tests that set position cover command delegates to API."""
|
||||
await setup_platform(
|
||||
hass,
|
||||
COVER_DOMAIN,
|
||||
shades_with_position("name-1"),
|
||||
bond_device_id="test-device-id",
|
||||
)
|
||||
|
||||
with patch_bond_action() as mock_hold, patch_bond_device_state(
|
||||
return_value={"position": 0, "open": 1}
|
||||
):
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 100},
|
||||
blocking=True,
|
||||
)
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_hold.assert_called_once_with("test-device-id", Action.set_position(0))
|
||||
entity_state = hass.states.get("cover.name_1")
|
||||
assert entity_state.state == STATE_OPEN
|
||||
assert entity_state.attributes[ATTR_CURRENT_POSITION] == 100
|
||||
|
||||
with patch_bond_action() as mock_hold, patch_bond_device_state(
|
||||
return_value={"position": 100, "open": 0}
|
||||
):
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 0},
|
||||
blocking=True,
|
||||
)
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_hold.assert_called_once_with("test-device-id", Action.set_position(100))
|
||||
entity_state = hass.states.get("cover.name_1")
|
||||
assert entity_state.state == STATE_CLOSED
|
||||
assert entity_state.attributes[ATTR_CURRENT_POSITION] == 0
|
||||
|
||||
with patch_bond_action() as mock_hold, patch_bond_device_state(
|
||||
return_value={"position": 40, "open": 1}
|
||||
):
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 60},
|
||||
blocking=True,
|
||||
)
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_hold.assert_called_once_with("test-device-id", Action.set_position(40))
|
||||
entity_state = hass.states.get("cover.name_1")
|
||||
assert entity_state.state == STATE_OPEN
|
||||
assert entity_state.attributes[ATTR_CURRENT_POSITION] == 60
|
||||
|
Loading…
x
Reference in New Issue
Block a user