Add support for WMS roller shutters and blinds (#132645)

* Add support for WMS roller shutters and blinds

* Add test variants for WMS device types and their diagnostics

* Add test variants for cover movement of WMS device types

* Move device entry tests to test_init and avoid snapshot list

Suggested-by: joostlek
This commit is contained in:
Marc Hörsken 2025-04-30 20:51:10 +02:00 committed by GitHub
parent 621cf6ce58
commit c3abf5a190
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1360 additions and 69 deletions

View File

@ -32,25 +32,29 @@ async def async_setup_entry(
entities: list[WebControlProGenericEntity] = [] entities: list[WebControlProGenericEntity] = []
for dest in hub.dests.values(): for dest in hub.dests.values():
if dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive): if dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive):
entities.append(WebControlProAwning(config_entry.entry_id, dest)) # noqa: PERF401 entities.append(WebControlProAwning(config_entry.entry_id, dest))
elif dest.action(
WMS_WebControl_pro_API_actionDescription.RollerShutterBlindDrive
):
entities.append(WebControlProRollerShutter(config_entry.entry_id, dest))
async_add_entities(entities) async_add_entities(entities)
class WebControlProAwning(WebControlProGenericEntity, CoverEntity): class WebControlProCover(WebControlProGenericEntity, CoverEntity):
"""Representation of a WMS based awning.""" """Base representation of a WMS based cover."""
_attr_device_class = CoverDeviceClass.AWNING _drive_action_desc: WMS_WebControl_pro_API_actionDescription
@property @property
def current_cover_position(self) -> int | None: def current_cover_position(self) -> int | None:
"""Return current position of cover.""" """Return current position of cover."""
action = self._dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive) action = self._dest.action(self._drive_action_desc)
return 100 - action["percentage"] return 100 - action["percentage"]
async def async_set_cover_position(self, **kwargs: Any) -> None: async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
action = self._dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive) action = self._dest.action(self._drive_action_desc)
await action(percentage=100 - kwargs[ATTR_POSITION]) await action(percentage=100 - kwargs[ATTR_POSITION])
@property @property
@ -60,12 +64,12 @@ class WebControlProAwning(WebControlProGenericEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover.""" """Open the cover."""
action = self._dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive) action = self._dest.action(self._drive_action_desc)
await action(percentage=0) await action(percentage=0)
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover.""" """Close the cover."""
action = self._dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive) action = self._dest.action(self._drive_action_desc)
await action(percentage=100) await action(percentage=100)
async def async_stop_cover(self, **kwargs: Any) -> None: async def async_stop_cover(self, **kwargs: Any) -> None:
@ -75,3 +79,19 @@ class WebControlProAwning(WebControlProGenericEntity, CoverEntity):
WMS_WebControl_pro_API_actionType.Stop, WMS_WebControl_pro_API_actionType.Stop,
) )
await action() await action()
class WebControlProAwning(WebControlProCover):
"""Representation of a WMS based awning."""
_attr_device_class = CoverDeviceClass.AWNING
_drive_action_desc = WMS_WebControl_pro_API_actionDescription.AwningDrive
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
)

View File

@ -55,17 +55,29 @@ def mock_hub_configuration_test() -> Generator[AsyncMock]:
"""Override WebControlPro.configuration.""" """Override WebControlPro.configuration."""
with patch( with patch(
"wmspro.webcontrol.WebControlPro._getConfiguration", "wmspro.webcontrol.WebControlPro._getConfiguration",
return_value=load_json_object_fixture("example_config_test.json", DOMAIN), return_value=load_json_object_fixture("config_test.json", DOMAIN),
) as mock_hub_configuration: ) as mock_hub_configuration:
yield mock_hub_configuration yield mock_hub_configuration
@pytest.fixture @pytest.fixture
def mock_hub_configuration_prod() -> Generator[AsyncMock]: def mock_hub_configuration_prod_awning_dimmer() -> Generator[AsyncMock]:
"""Override WebControlPro._getConfiguration.""" """Override WebControlPro._getConfiguration."""
with patch( with patch(
"wmspro.webcontrol.WebControlPro._getConfiguration", "wmspro.webcontrol.WebControlPro._getConfiguration",
return_value=load_json_object_fixture("example_config_prod.json", DOMAIN), return_value=load_json_object_fixture("config_prod_awning_dimmer.json", DOMAIN),
) as mock_hub_configuration:
yield mock_hub_configuration
@pytest.fixture
def mock_hub_configuration_prod_roller_shutter() -> Generator[AsyncMock]:
"""Override WebControlPro._getConfiguration."""
with patch(
"wmspro.webcontrol.WebControlPro._getConfiguration",
return_value=load_json_object_fixture(
"config_prod_roller_shutter.json", DOMAIN
),
) as mock_hub_configuration: ) as mock_hub_configuration:
yield mock_hub_configuration yield mock_hub_configuration
@ -75,23 +87,31 @@ def mock_hub_status_prod_awning() -> Generator[AsyncMock]:
"""Override WebControlPro._getStatus.""" """Override WebControlPro._getStatus."""
with patch( with patch(
"wmspro.webcontrol.WebControlPro._getStatus", "wmspro.webcontrol.WebControlPro._getStatus",
return_value=load_json_object_fixture( return_value=load_json_object_fixture("status_prod_awning.json", DOMAIN),
"example_status_prod_awning.json", DOMAIN ) as mock_hub_status:
), yield mock_hub_status
) as mock_dest_refresh:
yield mock_dest_refresh
@pytest.fixture @pytest.fixture
def mock_hub_status_prod_dimmer() -> Generator[AsyncMock]: def mock_hub_status_prod_dimmer() -> Generator[AsyncMock]:
"""Override WebControlPro._getStatus."""
with patch(
"wmspro.webcontrol.WebControlPro._getStatus",
return_value=load_json_object_fixture("status_prod_dimmer.json", DOMAIN),
) as mock_hub_status:
yield mock_hub_status
@pytest.fixture
def mock_hub_status_prod_roller_shutter() -> Generator[AsyncMock]:
"""Override WebControlPro._getStatus.""" """Override WebControlPro._getStatus."""
with patch( with patch(
"wmspro.webcontrol.WebControlPro._getStatus", "wmspro.webcontrol.WebControlPro._getStatus",
return_value=load_json_object_fixture( return_value=load_json_object_fixture(
"example_status_prod_dimmer.json", DOMAIN "status_prod_roller_shutter.json", DOMAIN
), ),
) as mock_dest_refresh: ) as mock_hub_status:
yield mock_dest_refresh yield mock_hub_status
@pytest.fixture @pytest.fixture
@ -100,8 +120,8 @@ def mock_dest_refresh() -> Generator[AsyncMock]:
with patch( with patch(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
) as mock_dest_refresh: ) as mock_hub_status:
yield mock_dest_refresh yield mock_hub_status
@pytest.fixture @pytest.fixture

View File

@ -0,0 +1,171 @@
{
"command": "getConfiguration",
"protocolVersion": "1.0.0",
"destinations": [
{
"id": 18894,
"animationType": 2,
"names": ["Wohnebene alle", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
},
{
"id": 116682,
"animationType": 2,
"names": ["Wohnzimmer", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
},
{
"id": 172555,
"animationType": 2,
"names": ["Badezimmer", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
},
{
"id": 230952,
"animationType": 2,
"names": ["Sportzimmer", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
},
{
"id": 284942,
"animationType": 2,
"names": ["Terrasse", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
},
{
"id": 328518,
"animationType": 2,
"names": ["alle Rolll\u00e4den", "", "", ""],
"actions": [
{
"id": 0,
"actionType": 0,
"actionDescription": 4,
"minValue": 0,
"maxValue": 100
},
{
"id": 16,
"actionType": 6,
"actionDescription": 12
},
{
"id": 22,
"actionType": 8,
"actionDescription": 13
}
]
}
],
"rooms": [
{
"id": 15175,
"name": "Wohnbereich",
"destinations": [18894, 116682, 172555, 230952],
"scenes": []
},
{
"id": 92218,
"name": "Terrasse",
"destinations": [284942],
"scenes": []
},
{
"id": 193582,
"name": "Alle",
"destinations": [328518],
"scenes": []
}
],
"scenes": []
}

View File

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

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_diagnostics # name: test_diagnostics[mock_hub_configuration_prod_awning_dimmer]
dict({ dict({
'config': dict({ 'config': dict({
'command': 'getConfiguration', 'command': 'getConfiguration',
@ -242,3 +242,540 @@
}), }),
}) })
# --- # ---
# name: test_diagnostics[mock_hub_configuration_prod_roller_shutter]
dict({
'config': dict({
'command': 'getConfiguration',
'destinations': list([
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 18894,
'names': list([
'Wohnebene alle',
'',
'',
'',
]),
}),
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 116682,
'names': list([
'Wohnzimmer',
'',
'',
'',
]),
}),
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 172555,
'names': list([
'Badezimmer',
'',
'',
'',
]),
}),
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 230952,
'names': list([
'Sportzimmer',
'',
'',
'',
]),
}),
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 284942,
'names': list([
'Terrasse',
'',
'',
'',
]),
}),
dict({
'actions': list([
dict({
'actionDescription': 4,
'actionType': 0,
'id': 0,
'maxValue': 100,
'minValue': 0,
}),
dict({
'actionDescription': 12,
'actionType': 6,
'id': 16,
}),
dict({
'actionDescription': 13,
'actionType': 8,
'id': 22,
}),
]),
'animationType': 2,
'id': 328518,
'names': list([
'alle Rollläden',
'',
'',
'',
]),
}),
]),
'protocolVersion': '1.0.0',
'rooms': list([
dict({
'destinations': list([
18894,
116682,
172555,
230952,
]),
'id': 15175,
'name': 'Wohnbereich',
'scenes': list([
]),
}),
dict({
'destinations': list([
284942,
]),
'id': 92218,
'name': 'Terrasse',
'scenes': list([
]),
}),
dict({
'destinations': list([
328518,
]),
'id': 193582,
'name': 'Alle',
'scenes': list([
]),
}),
]),
'scenes': list([
]),
}),
'dests': dict({
'116682': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 116682,
'name': 'Wohnzimmer',
'room': dict({
'15175': 'Wohnbereich',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
'172555': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 172555,
'name': 'Badezimmer',
'room': dict({
'15175': 'Wohnbereich',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
'18894': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 18894,
'name': 'Wohnebene alle',
'room': dict({
'15175': 'Wohnbereich',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
'230952': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 230952,
'name': 'Sportzimmer',
'room': dict({
'15175': 'Wohnbereich',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
'284942': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 284942,
'name': 'Terrasse',
'room': dict({
'92218': 'Terrasse',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
'328518': dict({
'actions': dict({
'0': dict({
'actionDescription': 'RollerShutterBlindDrive',
'actionType': 'Percentage',
'attrs': dict({
'maxValue': 100,
'minValue': 0,
}),
'id': 0,
'params': dict({
}),
}),
'16': dict({
'actionDescription': 'ManualCommand',
'actionType': 'Stop',
'attrs': dict({
}),
'id': 16,
'params': dict({
}),
}),
'22': dict({
'actionDescription': 'Identify',
'actionType': 'Identify',
'attrs': dict({
}),
'id': 22,
'params': dict({
}),
}),
}),
'animationType': 'RollerShutterBlind',
'available': True,
'blocking': None,
'drivingCause': 'Unknown',
'heartbeatError': None,
'id': 328518,
'name': 'alle Rollläden',
'room': dict({
'193582': 'Alle',
}),
'status': dict({
}),
'unknownProducts': dict({
}),
}),
}),
'host': 'webcontrol',
'rooms': dict({
'15175': dict({
'destinations': dict({
'116682': 'Wohnzimmer',
'172555': 'Badezimmer',
'18894': 'Wohnebene alle',
'230952': 'Sportzimmer',
}),
'id': 15175,
'name': 'Wohnbereich',
'scenes': dict({
}),
}),
'193582': dict({
'destinations': dict({
'328518': 'alle Rollläden',
}),
'id': 193582,
'name': 'Alle',
'scenes': dict({
}),
}),
'92218': dict({
'destinations': dict({
'284942': 'Terrasse',
}),
'id': 92218,
'name': 'Terrasse',
'scenes': dict({
}),
}),
}),
'scenes': dict({
}),
})
# ---

View File

@ -0,0 +1,397 @@
# serializer version: 1
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_awning][device-19239]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'19239',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Room',
'model_id': None,
'name': 'Terrasse',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '19239',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_awning][device-58717]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'58717',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Awning',
'model_id': None,
'name': 'Markise',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '58717',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_awning][device-97358]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'97358',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Dimmer',
'model_id': None,
'name': 'Licht',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '97358',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_dimmer][device-19239]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'19239',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Room',
'model_id': None,
'name': 'Terrasse',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '19239',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_dimmer][device-58717]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'58717',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Awning',
'model_id': None,
'name': 'Markise',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '58717',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_awning_dimmer-mock_hub_status_prod_dimmer][device-97358]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'97358',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Dimmer',
'model_id': None,
'name': 'Licht',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '97358',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-116682]
DeviceRegistryEntrySnapshot({
'area_id': 'wohnbereich',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'116682',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'Wohnzimmer',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '116682',
'suggested_area': 'Wohnbereich',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-172555]
DeviceRegistryEntrySnapshot({
'area_id': 'wohnbereich',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'172555',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'Badezimmer',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '172555',
'suggested_area': 'Wohnbereich',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-18894]
DeviceRegistryEntrySnapshot({
'area_id': 'wohnbereich',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'18894',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'Wohnebene alle',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '18894',
'suggested_area': 'Wohnbereich',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-230952]
DeviceRegistryEntrySnapshot({
'area_id': 'wohnbereich',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'230952',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'Sportzimmer',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '230952',
'suggested_area': 'Wohnbereich',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-284942]
DeviceRegistryEntrySnapshot({
'area_id': 'terrasse',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'284942',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'Terrasse',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '284942',
'suggested_area': 'Terrasse',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---
# name: test_cover_device[mock_hub_configuration_prod_roller_shutter-mock_hub_status_prod_roller_shutter][device-328518]
DeviceRegistryEntrySnapshot({
'area_id': 'alle',
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'328518',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'RollerShutterBlind',
'model_id': None,
'name': 'alle Rollläden',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '328518',
'suggested_area': 'Alle',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---

View File

@ -367,13 +367,15 @@ async def test_config_flow_multiple_entries(
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_dest_refresh: AsyncMock, mock_dest_refresh: AsyncMock,
mock_hub_configuration_test: AsyncMock, mock_hub_configuration_test: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
) -> None: ) -> None:
"""Test we allow creation of different config entries.""" """Test we allow creation of different config entries."""
await setup_config_entry(hass, mock_config_entry) await setup_config_entry(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED assert mock_config_entry.state is ConfigEntryState.LOADED
mock_hub_configuration_prod.return_value = mock_hub_configuration_test.return_value mock_hub_configuration_prod_awning_dimmer.return_value = (
mock_hub_configuration_test.return_value
)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}

View File

@ -3,6 +3,7 @@
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.components.wmspro.const import DOMAIN from homeassistant.components.wmspro.const import DOMAIN
@ -29,7 +30,7 @@ async def test_cover_device(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_awning: AsyncMock, mock_hub_status_prod_awning: AsyncMock,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
@ -37,7 +38,7 @@ async def test_cover_device(
"""Test that a cover device is created correctly.""" """Test that a cover device is created correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_awning.mock_calls) == 2 assert len(mock_hub_status_prod_awning.mock_calls) == 2
device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "58717")}) device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "58717")})
@ -49,7 +50,7 @@ async def test_cover_update(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_awning: AsyncMock, mock_hub_status_prod_awning: AsyncMock,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
@ -57,7 +58,7 @@ async def test_cover_update(
"""Test that a cover entity is created and updated correctly.""" """Test that a cover entity is created and updated correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_awning.mock_calls) == 2 assert len(mock_hub_status_prod_awning.mock_calls) == 2
entity = hass.states.get("cover.markise") entity = hass.states.get("cover.markise")
@ -72,21 +73,41 @@ async def test_cover_update(
assert len(mock_hub_status_prod_awning.mock_calls) >= 3 assert len(mock_hub_status_prod_awning.mock_calls) >= 3
@pytest.mark.parametrize(
("mock_hub_configuration", "mock_hub_status", "entity_name"),
[
(
"mock_hub_configuration_prod_awning_dimmer",
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
"cover.wohnebene_alle",
),
],
)
async def test_cover_open_and_close( async def test_cover_open_and_close(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration: AsyncMock,
mock_hub_status_prod_awning: AsyncMock, mock_hub_status: AsyncMock,
mock_action_call: AsyncMock, mock_action_call: AsyncMock,
request: pytest.FixtureRequest,
entity_name: str,
) -> None: ) -> None:
"""Test that a cover entity is opened and closed correctly.""" """Test that a cover entity is opened and closed correctly."""
mock_hub_configuration = request.getfixturevalue(mock_hub_configuration)
mock_hub_status = request.getfixturevalue(mock_hub_status)
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration.mock_calls) == 1
assert len(mock_hub_status_prod_awning.mock_calls) >= 1 assert len(mock_hub_status.mock_calls) >= 1
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_CLOSED assert entity.state == STATE_CLOSED
assert entity.attributes["current_position"] == 0 assert entity.attributes["current_position"] == 0
@ -95,7 +116,7 @@ async def test_cover_open_and_close(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
): ):
before = len(mock_hub_status_prod_awning.mock_calls) before = len(mock_hub_status.mock_calls)
await hass.services.async_call( await hass.services.async_call(
Platform.COVER, Platform.COVER,
@ -104,17 +125,17 @@ async def test_cover_open_and_close(
blocking=True, blocking=True,
) )
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_OPEN assert entity.state == STATE_OPEN
assert entity.attributes["current_position"] == 100 assert entity.attributes["current_position"] == 100
assert len(mock_hub_status_prod_awning.mock_calls) == before assert len(mock_hub_status.mock_calls) == before
with patch( with patch(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
): ):
before = len(mock_hub_status_prod_awning.mock_calls) before = len(mock_hub_status.mock_calls)
await hass.services.async_call( await hass.services.async_call(
Platform.COVER, Platform.COVER,
@ -123,28 +144,48 @@ async def test_cover_open_and_close(
blocking=True, blocking=True,
) )
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_CLOSED assert entity.state == STATE_CLOSED
assert entity.attributes["current_position"] == 0 assert entity.attributes["current_position"] == 0
assert len(mock_hub_status_prod_awning.mock_calls) == before assert len(mock_hub_status.mock_calls) == before
@pytest.mark.parametrize(
("mock_hub_configuration", "mock_hub_status", "entity_name"),
[
(
"mock_hub_configuration_prod_awning_dimmer",
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
"cover.wohnebene_alle",
),
],
)
async def test_cover_open_to_pos( async def test_cover_open_to_pos(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration: AsyncMock,
mock_hub_status_prod_awning: AsyncMock, mock_hub_status: AsyncMock,
mock_action_call: AsyncMock, mock_action_call: AsyncMock,
request: pytest.FixtureRequest,
entity_name: str,
) -> None: ) -> None:
"""Test that a cover entity is opened to correct position.""" """Test that a cover entity is opened to correct position."""
mock_hub_configuration = request.getfixturevalue(mock_hub_configuration)
mock_hub_status = request.getfixturevalue(mock_hub_status)
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration.mock_calls) == 1
assert len(mock_hub_status_prod_awning.mock_calls) >= 1 assert len(mock_hub_status.mock_calls) >= 1
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_CLOSED assert entity.state == STATE_CLOSED
assert entity.attributes["current_position"] == 0 assert entity.attributes["current_position"] == 0
@ -153,7 +194,7 @@ async def test_cover_open_to_pos(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
): ):
before = len(mock_hub_status_prod_awning.mock_calls) before = len(mock_hub_status.mock_calls)
await hass.services.async_call( await hass.services.async_call(
Platform.COVER, Platform.COVER,
@ -162,28 +203,48 @@ async def test_cover_open_to_pos(
blocking=True, blocking=True,
) )
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_OPEN assert entity.state == STATE_OPEN
assert entity.attributes["current_position"] == 50 assert entity.attributes["current_position"] == 50
assert len(mock_hub_status_prod_awning.mock_calls) == before assert len(mock_hub_status.mock_calls) == before
@pytest.mark.parametrize(
("mock_hub_configuration", "mock_hub_status", "entity_name"),
[
(
"mock_hub_configuration_prod_awning_dimmer",
"mock_hub_status_prod_awning",
"cover.markise",
),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
"cover.wohnebene_alle",
),
],
)
async def test_cover_open_and_stop( async def test_cover_open_and_stop(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration: AsyncMock,
mock_hub_status_prod_awning: AsyncMock, mock_hub_status: AsyncMock,
mock_action_call: AsyncMock, mock_action_call: AsyncMock,
request: pytest.FixtureRequest,
entity_name: str,
) -> None: ) -> None:
"""Test that a cover entity is opened and stopped correctly.""" """Test that a cover entity is opened and stopped correctly."""
mock_hub_configuration = request.getfixturevalue(mock_hub_configuration)
mock_hub_status = request.getfixturevalue(mock_hub_status)
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration.mock_calls) == 1
assert len(mock_hub_status_prod_awning.mock_calls) >= 1 assert len(mock_hub_status.mock_calls) >= 1
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_CLOSED assert entity.state == STATE_CLOSED
assert entity.attributes["current_position"] == 0 assert entity.attributes["current_position"] == 0
@ -192,7 +253,7 @@ async def test_cover_open_and_stop(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
): ):
before = len(mock_hub_status_prod_awning.mock_calls) before = len(mock_hub_status.mock_calls)
await hass.services.async_call( await hass.services.async_call(
Platform.COVER, Platform.COVER,
@ -201,17 +262,17 @@ async def test_cover_open_and_stop(
blocking=True, blocking=True,
) )
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_OPEN assert entity.state == STATE_OPEN
assert entity.attributes["current_position"] == 80 assert entity.attributes["current_position"] == 80
assert len(mock_hub_status_prod_awning.mock_calls) == before assert len(mock_hub_status.mock_calls) == before
with patch( with patch(
"wmspro.destination.Destination.refresh", "wmspro.destination.Destination.refresh",
return_value=True, return_value=True,
): ):
before = len(mock_hub_status_prod_awning.mock_calls) before = len(mock_hub_status.mock_calls)
await hass.services.async_call( await hass.services.async_call(
Platform.COVER, Platform.COVER,
@ -220,8 +281,8 @@ async def test_cover_open_and_stop(
blocking=True, blocking=True,
) )
entity = hass.states.get("cover.markise") entity = hass.states.get(entity_name)
assert entity is not None assert entity is not None
assert entity.state == STATE_OPEN assert entity.state == STATE_OPEN
assert entity.attributes["current_position"] == 80 assert entity.attributes["current_position"] == 80
assert len(mock_hub_status_prod_awning.mock_calls) == before assert len(mock_hub_status.mock_calls) == before

View File

@ -2,6 +2,7 @@
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -13,20 +14,30 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@pytest.mark.parametrize(
("mock_hub_configuration"),
[
("mock_hub_configuration_prod_awning_dimmer"),
("mock_hub_configuration_prod_roller_shutter"),
],
)
async def test_diagnostics( async def test_diagnostics(
hass: HomeAssistant, hass: HomeAssistant,
hass_client: ClientSessionGenerator, hass_client: ClientSessionGenerator,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration: AsyncMock,
mock_dest_refresh: AsyncMock, mock_dest_refresh: AsyncMock,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
request: pytest.FixtureRequest,
) -> None: ) -> None:
"""Test that a config entry can be loaded with DeviceConfig.""" """Test that a config entry can be loaded with DeviceConfig."""
mock_hub_configuration = request.getfixturevalue(mock_hub_configuration)
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration.mock_calls) == 1
assert len(mock_dest_refresh.mock_calls) == 2 assert len(mock_dest_refresh.mock_calls) > 0
result = await get_diagnostics_for_config_entry( result = await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry hass, hass_client, mock_config_entry

View File

@ -3,9 +3,13 @@
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
import aiohttp import aiohttp
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.wmspro.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from . import setup_config_entry from . import setup_config_entry
@ -36,3 +40,49 @@ async def test_config_entry_device_config_refresh_failed(
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_refresh.mock_calls) == 1 assert len(mock_hub_refresh.mock_calls) == 1
@pytest.mark.parametrize(
("mock_hub_configuration", "mock_hub_status"),
[
("mock_hub_configuration_prod_awning_dimmer", "mock_hub_status_prod_awning"),
("mock_hub_configuration_prod_awning_dimmer", "mock_hub_status_prod_dimmer"),
(
"mock_hub_configuration_prod_roller_shutter",
"mock_hub_status_prod_roller_shutter",
),
],
)
async def test_cover_device(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock,
mock_hub_configuration: AsyncMock,
mock_hub_status: AsyncMock,
device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion,
request: pytest.FixtureRequest,
) -> None:
"""Test that the device is created correctly."""
mock_hub_configuration = request.getfixturevalue(mock_hub_configuration)
mock_hub_status = request.getfixturevalue(mock_hub_status)
assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration.mock_calls) == 1
assert len(mock_hub_status.mock_calls) > 0
device_entries = device_registry.devices.get_devices_for_config_entry_id(
mock_config_entry.entry_id
)
assert len(device_entries) > 1
device_entries = list(
filter(
lambda e: e.identifiers != {(DOMAIN, mock_config_entry.entry_id)},
device_entries,
)
)
assert len(device_entries) > 0
for device_entry in device_entries:
assert device_entry == snapshot(name=f"device-{device_entry.serial_number}")

View File

@ -28,7 +28,7 @@ async def test_light_device(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_dimmer: AsyncMock, mock_hub_status_prod_dimmer: AsyncMock,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
@ -36,7 +36,7 @@ async def test_light_device(
"""Test that a light device is created correctly.""" """Test that a light device is created correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_dimmer.mock_calls) == 2 assert len(mock_hub_status_prod_dimmer.mock_calls) == 2
device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "97358")}) device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "97358")})
@ -48,7 +48,7 @@ async def test_light_update(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_dimmer: AsyncMock, mock_hub_status_prod_dimmer: AsyncMock,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
@ -56,7 +56,7 @@ async def test_light_update(
"""Test that a light entity is created and updated correctly.""" """Test that a light entity is created and updated correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_dimmer.mock_calls) == 2 assert len(mock_hub_status_prod_dimmer.mock_calls) == 2
entity = hass.states.get("light.licht") entity = hass.states.get("light.licht")
@ -75,14 +75,14 @@ async def test_light_turn_on_and_off(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_dimmer: AsyncMock, mock_hub_status_prod_dimmer: AsyncMock,
mock_action_call: AsyncMock, mock_action_call: AsyncMock,
) -> None: ) -> None:
"""Test that a light entity is turned on and off correctly.""" """Test that a light entity is turned on and off correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_dimmer.mock_calls) >= 1 assert len(mock_hub_status_prod_dimmer.mock_calls) >= 1
entity = hass.states.get("light.licht") entity = hass.states.get("light.licht")
@ -133,14 +133,14 @@ async def test_light_dimm_on_and_off(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock, mock_hub_ping: AsyncMock,
mock_hub_configuration_prod: AsyncMock, mock_hub_configuration_prod_awning_dimmer: AsyncMock,
mock_hub_status_prod_dimmer: AsyncMock, mock_hub_status_prod_dimmer: AsyncMock,
mock_action_call: AsyncMock, mock_action_call: AsyncMock,
) -> None: ) -> None:
"""Test that a light entity is dimmed on and off correctly.""" """Test that a light entity is dimmed on and off correctly."""
assert await setup_config_entry(hass, mock_config_entry) assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1 assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_prod.mock_calls) == 1 assert len(mock_hub_configuration_prod_awning_dimmer.mock_calls) == 1
assert len(mock_hub_status_prod_dimmer.mock_calls) >= 1 assert len(mock_hub_status_prod_dimmer.mock_calls) >= 1
entity = hass.states.get("light.licht") entity = hass.states.get("light.licht")