mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Use pydeconz interface controls for cover platform (#74535)
This commit is contained in:
parent
5e63a44e71
commit
b071affcb4
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, cast
|
||||
|
||||
from pydeconz.interfaces.lights import CoverAction
|
||||
from pydeconz.models.event import EventType
|
||||
from pydeconz.models.light.cover import Cover
|
||||
|
||||
@ -21,7 +22,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||
|
||||
DEVICE_CLASS = {
|
||||
DECONZ_TYPE_TO_DEVICE_CLASS = {
|
||||
"Level controllable output": CoverDeviceClass.DAMPER,
|
||||
"Window covering controller": CoverDeviceClass.SHADE,
|
||||
"Window covering device": CoverDeviceClass.SHADE,
|
||||
@ -40,8 +41,7 @@ async def async_setup_entry(
|
||||
@callback
|
||||
def async_add_cover(_: EventType, cover_id: str) -> None:
|
||||
"""Add cover from deCONZ."""
|
||||
cover = gateway.api.lights.covers[cover_id]
|
||||
async_add_entities([DeconzCover(cover, gateway)])
|
||||
async_add_entities([DeconzCover(cover_id, gateway)])
|
||||
|
||||
gateway.register_platform_add_device_callback(
|
||||
async_add_cover,
|
||||
@ -55,9 +55,9 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
TYPE = DOMAIN
|
||||
_device: Cover
|
||||
|
||||
def __init__(self, device: Cover, gateway: DeconzGateway) -> None:
|
||||
def __init__(self, cover_id: str, gateway: DeconzGateway) -> None:
|
||||
"""Set up cover device."""
|
||||
super().__init__(device, gateway)
|
||||
super().__init__(cover := gateway.api.lights.covers[cover_id], gateway)
|
||||
|
||||
self._attr_supported_features = CoverEntityFeature.OPEN
|
||||
self._attr_supported_features |= CoverEntityFeature.CLOSE
|
||||
@ -70,7 +70,7 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
self._attr_supported_features |= CoverEntityFeature.STOP_TILT
|
||||
self._attr_supported_features |= CoverEntityFeature.SET_TILT_POSITION
|
||||
|
||||
self._attr_device_class = DEVICE_CLASS.get(self._device.type)
|
||||
self._attr_device_class = DECONZ_TYPE_TO_DEVICE_CLASS.get(cover.type)
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
@ -85,19 +85,31 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Move the cover to a specific position."""
|
||||
position = 100 - cast(int, kwargs[ATTR_POSITION])
|
||||
await self._device.set_position(lift=position)
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
lift=position,
|
||||
)
|
||||
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open cover."""
|
||||
await self._device.open()
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.OPEN,
|
||||
)
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close cover."""
|
||||
await self._device.close()
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.CLOSE,
|
||||
)
|
||||
|
||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||
"""Stop cover."""
|
||||
await self._device.stop()
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.STOP,
|
||||
)
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self) -> int | None:
|
||||
@ -109,16 +121,28 @@ class DeconzCover(DeconzDevice, CoverEntity):
|
||||
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
|
||||
"""Tilt the cover to a specific position."""
|
||||
position = 100 - cast(int, kwargs[ATTR_TILT_POSITION])
|
||||
await self._device.set_position(tilt=position)
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
tilt=position,
|
||||
)
|
||||
|
||||
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
|
||||
"""Open cover tilt."""
|
||||
await self._device.set_position(tilt=0)
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
tilt=0,
|
||||
)
|
||||
|
||||
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
|
||||
"""Close cover tilt."""
|
||||
await self._device.set_position(tilt=100)
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
tilt=100,
|
||||
)
|
||||
|
||||
async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
|
||||
"""Stop cover tilt."""
|
||||
await self._device.stop()
|
||||
await self.gateway.api.lights.covers.set_state(
|
||||
id=self._device.resource_id,
|
||||
action=CoverAction.STOP,
|
||||
)
|
||||
|
@ -42,68 +42,48 @@ async def test_cover(hass, aioclient_mock, mock_deconz_websocket):
|
||||
data = {
|
||||
"lights": {
|
||||
"1": {
|
||||
"name": "Level controllable cover",
|
||||
"type": "Level controllable output",
|
||||
"state": {"bri": 254, "on": False, "reachable": True},
|
||||
"modelid": "Not zigbee spec",
|
||||
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
||||
},
|
||||
"2": {
|
||||
"name": "Window covering device",
|
||||
"type": "Window covering device",
|
||||
"state": {"lift": 100, "open": False, "reachable": True},
|
||||
"modelid": "lumi.curtain",
|
||||
"uniqueid": "00:00:00:00:00:00:00:01-00",
|
||||
},
|
||||
"3": {
|
||||
"2": {
|
||||
"name": "Unsupported cover",
|
||||
"type": "Not a cover",
|
||||
"state": {"reachable": True},
|
||||
"uniqueid": "00:00:00:00:00:00:00:02-00",
|
||||
},
|
||||
"4": {
|
||||
"name": "deconz old brightness cover",
|
||||
"type": "Level controllable output",
|
||||
"state": {"bri": 255, "on": False, "reachable": True},
|
||||
"modelid": "Not zigbee spec",
|
||||
"uniqueid": "00:00:00:00:00:00:00:03-00",
|
||||
},
|
||||
"5": {
|
||||
"name": "Window covering controller",
|
||||
"type": "Window covering controller",
|
||||
"state": {"bri": 253, "on": True, "reachable": True},
|
||||
"modelid": "Motor controller",
|
||||
"uniqueid": "00:00:00:00:00:00:00:04-00",
|
||||
},
|
||||
}
|
||||
}
|
||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
||||
|
||||
assert len(hass.states.async_all()) == 5
|
||||
assert hass.states.get("cover.level_controllable_cover").state == STATE_OPEN
|
||||
assert hass.states.get("cover.window_covering_device").state == STATE_CLOSED
|
||||
assert len(hass.states.async_all()) == 2
|
||||
cover = hass.states.get("cover.window_covering_device")
|
||||
assert cover.state == STATE_CLOSED
|
||||
assert cover.attributes[ATTR_CURRENT_POSITION] == 0
|
||||
assert not hass.states.get("cover.unsupported_cover")
|
||||
assert hass.states.get("cover.deconz_old_brightness_cover").state == STATE_OPEN
|
||||
assert hass.states.get("cover.window_covering_controller").state == STATE_CLOSED
|
||||
|
||||
# Event signals cover is closed
|
||||
# Event signals cover is open
|
||||
|
||||
event_changed_light = {
|
||||
"t": "event",
|
||||
"e": "changed",
|
||||
"r": "lights",
|
||||
"id": "1",
|
||||
"state": {"on": True},
|
||||
"state": {"lift": 0, "open": True},
|
||||
}
|
||||
await mock_deconz_websocket(data=event_changed_light)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("cover.level_controllable_cover").state == STATE_CLOSED
|
||||
cover = hass.states.get("cover.window_covering_device")
|
||||
assert cover.state == STATE_OPEN
|
||||
assert cover.attributes[ATTR_CURRENT_POSITION] == 100
|
||||
|
||||
# Verify service calls for cover
|
||||
|
||||
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/2/state")
|
||||
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1/state")
|
||||
|
||||
# Service open cover
|
||||
|
||||
@ -145,71 +125,10 @@ async def test_cover(hass, aioclient_mock, mock_deconz_websocket):
|
||||
)
|
||||
assert aioclient_mock.mock_calls[4][2] == {"stop": True}
|
||||
|
||||
# Verify service calls for legacy cover
|
||||
|
||||
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1/state")
|
||||
|
||||
# Service open cover
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_OPEN_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.level_controllable_cover"},
|
||||
blocking=True,
|
||||
)
|
||||
assert aioclient_mock.mock_calls[5][2] == {"on": False}
|
||||
|
||||
# Service close cover
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.level_controllable_cover"},
|
||||
blocking=True,
|
||||
)
|
||||
assert aioclient_mock.mock_calls[6][2] == {"on": True}
|
||||
|
||||
# Service set cover position
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_ENTITY_ID: "cover.level_controllable_cover", ATTR_POSITION: 40},
|
||||
blocking=True,
|
||||
)
|
||||
assert aioclient_mock.mock_calls[7][2] == {"bri": 152}
|
||||
|
||||
# Service stop cover movement
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_STOP_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.level_controllable_cover"},
|
||||
blocking=True,
|
||||
)
|
||||
assert aioclient_mock.mock_calls[8][2] == {"bri_inc": 0}
|
||||
|
||||
# Test that a reported cover position of 255 (deconz-rest-api < 2.05.73) is interpreted correctly.
|
||||
assert hass.states.get("cover.deconz_old_brightness_cover").state == STATE_OPEN
|
||||
|
||||
event_changed_light = {
|
||||
"t": "event",
|
||||
"e": "changed",
|
||||
"r": "lights",
|
||||
"id": "4",
|
||||
"state": {"on": True},
|
||||
}
|
||||
await mock_deconz_websocket(data=event_changed_light)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
deconz_old_brightness_cover = hass.states.get("cover.deconz_old_brightness_cover")
|
||||
assert deconz_old_brightness_cover.state == STATE_CLOSED
|
||||
assert deconz_old_brightness_cover.attributes[ATTR_CURRENT_POSITION] == 0
|
||||
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
states = hass.states.async_all()
|
||||
assert len(states) == 5
|
||||
assert len(states) == 2
|
||||
for state in states:
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user