1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Add support for valance shades / volants to WMS WebControl pro (#150882)

This commit is contained in:
Marc Hörsken
2025-09-11 12:57:53 +02:00
committed by GitHub
parent 50349e49f1
commit 343b17788f
5 changed files with 135 additions and 11 deletions

View File

@@ -6,10 +6,11 @@ from datetime import timedelta
from typing import Any
from wmspro.const import (
WMS_WebControl_pro_API_actionDescription,
WMS_WebControl_pro_API_actionDescription as ACTION_DESC,
WMS_WebControl_pro_API_actionType,
WMS_WebControl_pro_API_responseType,
)
from wmspro.destination import Destination
from homeassistant.components.cover import ATTR_POSITION, CoverDeviceClass, CoverEntity
from homeassistant.core import HomeAssistant
@@ -32,11 +33,11 @@ async def async_setup_entry(
entities: list[WebControlProGenericEntity] = []
for dest in hub.dests.values():
if dest.hasAction(WMS_WebControl_pro_API_actionDescription.AwningDrive):
if dest.hasAction(ACTION_DESC.AwningDrive):
entities.append(WebControlProAwning(config_entry.entry_id, dest))
elif dest.hasAction(
WMS_WebControl_pro_API_actionDescription.RollerShutterBlindDrive
):
if dest.hasAction(ACTION_DESC.ValanceDrive):
entities.append(WebControlProValance(config_entry.entry_id, dest))
if dest.hasAction(ACTION_DESC.RollerShutterBlindDrive):
entities.append(WebControlProRollerShutter(config_entry.entry_id, dest))
async_add_entities(entities)
@@ -45,7 +46,7 @@ async def async_setup_entry(
class WebControlProCover(WebControlProGenericEntity, CoverEntity):
"""Base representation of a WMS based cover."""
_drive_action_desc: WMS_WebControl_pro_API_actionDescription
_drive_action_desc: ACTION_DESC
_attr_name = None
@property
@@ -79,7 +80,7 @@ class WebControlProCover(WebControlProGenericEntity, CoverEntity):
async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the device if in motion."""
action = self._dest.action(
WMS_WebControl_pro_API_actionDescription.ManualCommand,
ACTION_DESC.ManualCommand,
WMS_WebControl_pro_API_actionType.Stop,
)
await action(responseType=WMS_WebControl_pro_API_responseType.Detailed)
@@ -89,13 +90,25 @@ class WebControlProAwning(WebControlProCover):
"""Representation of a WMS based awning."""
_attr_device_class = CoverDeviceClass.AWNING
_drive_action_desc = WMS_WebControl_pro_API_actionDescription.AwningDrive
_drive_action_desc = ACTION_DESC.AwningDrive
class WebControlProValance(WebControlProCover):
"""Representation of a WMS based valance."""
_attr_translation_key = "valance"
_attr_device_class = CoverDeviceClass.SHADE
_drive_action_desc = ACTION_DESC.ValanceDrive
def __init__(self, config_entry_id: str, dest: Destination) -> None:
"""Initialize the entity with destination channel."""
super().__init__(config_entry_id, dest)
if self._attr_unique_id:
self._attr_unique_id += "-valance"
class WebControlProRollerShutter(WebControlProCover):
"""Representation of a WMS based roller shutter or blind."""
_attr_device_class = CoverDeviceClass.SHUTTER
_drive_action_desc = (
WMS_WebControl_pro_API_actionDescription.RollerShutterBlindDrive
)
_drive_action_desc = ACTION_DESC.RollerShutterBlindDrive

View File

@@ -70,6 +70,18 @@ def mock_hub_configuration_prod_awning_dimmer() -> Generator[AsyncMock]:
yield mock_hub_configuration
@pytest.fixture
def mock_hub_configuration_prod_awning_valance() -> Generator[AsyncMock]:
"""Override WebControlPro._getConfiguration."""
with patch(
"wmspro.webcontrol.WebControlPro._getConfiguration",
return_value=load_json_object_fixture(
"config_prod_awning_valance.json", DOMAIN
),
) as mock_hub_configuration:
yield mock_hub_configuration
@pytest.fixture
def mock_hub_configuration_prod_roller_shutter() -> Generator[AsyncMock]:
"""Override WebControlPro._getConfiguration."""
@@ -114,6 +126,16 @@ def mock_hub_status_prod_roller_shutter() -> Generator[AsyncMock]:
yield mock_hub_status
@pytest.fixture
def mock_hub_status_prod_valance() -> Generator[AsyncMock]:
"""Override WebControlPro._getStatus."""
with patch(
"wmspro.webcontrol.WebControlPro._getStatus",
return_value=load_json_object_fixture("status_prod_valance.json", DOMAIN),
) as mock_hub_status:
yield mock_hub_status
@pytest.fixture
def mock_dest_refresh() -> Generator[AsyncMock]:
"""Override Destination.refresh."""

View File

@@ -0,0 +1,46 @@
{
"command": "getConfiguration",
"protocolVersion": "1.0.0",
"destinations": [
{
"id": 58717,
"animationType": 1,
"names": ["Markise", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 0,
"minValue": 0,
"maxValue": 100
},
{
"id": 2,
"actionType": 0,
"actionDescription": 1,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
}
],
"rooms": [
{
"id": 62571,
"name": "Raum 0",
"destinations": [58717],
"scenes": []
}
],
"scenes": []
}

View File

@@ -0,0 +1,28 @@
{
"command": "getStatus",
"protocolVersion": "1.0.0",
"details": [
{
"destinationId": 58717,
"data": {
"drivingCause": 0,
"heartbeatError": false,
"blocking": false,
"productData": [
{
"actionId": 0,
"value": {
"percentage": 100
}
},
{
"actionId": 2,
"value": {
"percentage": 100
}
}
]
}
}
]
}

View File

@@ -81,6 +81,11 @@ async def test_cover_update(
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_awning_valance",
"mock_hub_status_prod_valance",
"cover.markise_2",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
@@ -159,6 +164,11 @@ async def test_cover_open_and_close(
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_awning_valance",
"mock_hub_status_prod_valance",
"cover.markise_2",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
@@ -218,6 +228,11 @@ async def test_cover_open_to_pos(
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_awning_valance",
"mock_hub_status_prod_valance",
"cover.markise_2",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",