mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +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,
|
mutually_exclusive=Action.OPEN,
|
||||||
argument=None,
|
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 bond_api import Action, BPUPSubscriptions, DeviceType
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
|
ATTR_POSITION,
|
||||||
CoverDeviceClass,
|
CoverDeviceClass,
|
||||||
CoverEntity,
|
CoverEntity,
|
||||||
CoverEntityFeature,
|
CoverEntityFeature,
|
||||||
@ -20,6 +21,16 @@ from .entity import BondEntity
|
|||||||
from .utils import BondDevice, BondHub
|
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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
@ -50,6 +61,8 @@ class BondCover(BondEntity, CoverEntity):
|
|||||||
"""Create HA entity representing Bond cover."""
|
"""Create HA entity representing Bond cover."""
|
||||||
super().__init__(hub, device, bpup_subs)
|
super().__init__(hub, device, bpup_subs)
|
||||||
supported_features = 0
|
supported_features = 0
|
||||||
|
if self._device.supports_set_position():
|
||||||
|
supported_features |= CoverEntityFeature.SET_POSITION
|
||||||
if self._device.supports_open():
|
if self._device.supports_open():
|
||||||
supported_features |= CoverEntityFeature.OPEN
|
supported_features |= CoverEntityFeature.OPEN
|
||||||
if self._device.supports_close():
|
if self._device.supports_close():
|
||||||
@ -67,8 +80,15 @@ class BondCover(BondEntity, CoverEntity):
|
|||||||
|
|
||||||
def _apply_state(self, state: dict) -> None:
|
def _apply_state(self, state: dict) -> None:
|
||||||
cover_open = state.get("open")
|
cover_open = state.get("open")
|
||||||
self._attr_is_closed = (
|
self._attr_is_closed = None if cover_open is None else cover_open == 0
|
||||||
True if cover_open == 0 else False if cover_open == 1 else None
|
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:
|
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 True if this device supports any of the direction related commands."""
|
||||||
return self._has_any_action({Action.SET_DIRECTION})
|
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:
|
def supports_open(self) -> bool:
|
||||||
"""Return True if this device supports opening."""
|
"""Return True if this device supports opening."""
|
||||||
return self._has_any_action({Action.OPEN})
|
return self._has_any_action({Action.OPEN})
|
||||||
|
@ -4,15 +4,22 @@ from datetime import timedelta
|
|||||||
from bond_api import Action, DeviceType
|
from bond_api import Action, DeviceType
|
||||||
|
|
||||||
from homeassistant import core
|
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 (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
SERVICE_CLOSE_COVER,
|
SERVICE_CLOSE_COVER,
|
||||||
SERVICE_CLOSE_COVER_TILT,
|
SERVICE_CLOSE_COVER_TILT,
|
||||||
SERVICE_OPEN_COVER,
|
SERVICE_OPEN_COVER,
|
||||||
SERVICE_OPEN_COVER_TILT,
|
SERVICE_OPEN_COVER_TILT,
|
||||||
|
SERVICE_SET_COVER_POSITION,
|
||||||
SERVICE_STOP_COVER,
|
SERVICE_STOP_COVER,
|
||||||
SERVICE_STOP_COVER_TILT,
|
SERVICE_STOP_COVER_TILT,
|
||||||
|
STATE_OPEN,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import entity_registry as er
|
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):
|
def tilt_only_shades(name: str):
|
||||||
"""Create motorized shades that only tilt."""
|
"""Create motorized shades that only tilt."""
|
||||||
return {
|
return {
|
||||||
@ -236,3 +252,64 @@ async def test_cover_available(hass: core.HomeAssistant):
|
|||||||
await help_test_entity_available(
|
await help_test_entity_available(
|
||||||
hass, COVER_DOMAIN, shades("name-1"), "cover.name_1"
|
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