Add scene support to WMS WebControl pro (#126081)

* Add scene support to WMS WebControl pro

* Update homeassistant/components/wmspro/scene.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Create a device per room instead of scene

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Marc Hörsken 2024-10-10 15:44:18 +02:00 committed by GitHub
parent f446e42317
commit 5e38bb7a32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 185 additions and 2 deletions

View File

@ -15,7 +15,7 @@ from homeassistant.helpers.typing import UNDEFINED
from .const import DOMAIN, MANUFACTURER
PLATFORMS: list[Platform] = [Platform.COVER]
PLATFORMS: list[Platform] = [Platform.COVER, Platform.SCENE]
type WebControlProConfigEntry = ConfigEntry[WebControlPro]

View File

@ -0,0 +1,64 @@
"""Support for scenes provided by WMS WebControl pro."""
from __future__ import annotations
from typing import Any
from wmspro.scene import Scene as WMS_Scene
from homeassistant.components.scene import Scene
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import WebControlProConfigEntry
from .const import ATTRIBUTION, DOMAIN, MANUFACTURER
async def async_setup_entry(
hass: HomeAssistant,
config_entry: WebControlProConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the WMS based scenes from a config entry."""
hub = config_entry.runtime_data
async_add_entities(
WebControlProScene(config_entry.entry_id, scene)
for scene in hub.scenes.values()
)
class WebControlProScene(Scene):
"""Representation of a WMS based scene."""
_attr_attribution = ATTRIBUTION
_attr_has_entity_name = True
def __init__(self, config_entry_id: str, scene: WMS_Scene) -> None:
"""Initialize the entity with the configured scene."""
super().__init__()
# Scene information
self._scene = scene
self._attr_name = scene.name
self._attr_unique_id = str(scene.id)
# Room information
room = scene.room
room_name = room.name
room_id_str = str(room.id)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, room_id_str)},
manufacturer=MANUFACTURER,
model="Room",
name=room_name,
serial_number=room_id_str,
suggested_area=room_name,
via_device=(DOMAIN, config_entry_id),
configuration_url=f"http://{scene.host}/control",
)
async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene. Try to get entities into requested state."""
await self._scene()

View File

@ -104,3 +104,12 @@ def mock_action_call() -> Generator[AsyncMock]:
fake_call,
) as mock_action_call:
yield mock_action_call
@pytest.fixture
def mock_scene_call() -> Generator[AsyncMock]:
"""Override Scene.__call__."""
with patch(
"wmspro.scene.Scene.__call__",
) as mock_scene_call:
yield mock_scene_call

View File

@ -0,0 +1,47 @@
# serializer version: 1
# name: test_scene_activate
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by WMS WebControl pro API',
'friendly_name': 'Raum 0 Gute Nacht',
}),
'context': <ANY>,
'entity_id': 'scene.raum_0_gute_nacht',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_scene_room_device
DeviceRegistryEntrySnapshot({
'area_id': 'raum_0',
'config_entries': <ANY>,
'configuration_url': 'http://webcontrol/control',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'wmspro',
'42581',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WAREMA Renkhoff SE',
'model': 'Room',
'model_id': None,
'name': 'Raum 0',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '42581',
'suggested_area': 'Raum 0',
'sw_version': None,
'via_device_id': <ANY>,
})
# ---

View File

@ -1,4 +1,4 @@
"""Test the wmspro diagnostics."""
"""Test the wmspro cover support."""
from unittest.mock import AsyncMock, patch

View File

@ -0,0 +1,63 @@
"""Test the wmspro scene support."""
from unittest.mock import AsyncMock
from syrupy import SnapshotAssertion
from homeassistant.components.wmspro.const import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component
from . import setup_config_entry
from tests.common import MockConfigEntry
async def test_scene_room_device(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock,
mock_hub_configuration_test: AsyncMock,
mock_dest_refresh: AsyncMock,
device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test that a scene room device is created correctly."""
assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_test.mock_calls) == 1
device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "42581")})
assert device_entry is not None
assert device_entry == snapshot
async def test_scene_activate(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hub_ping: AsyncMock,
mock_hub_configuration_test: AsyncMock,
mock_dest_refresh: AsyncMock,
mock_scene_call: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test that a scene entity is created and activated correctly."""
assert await setup_config_entry(hass, mock_config_entry)
assert len(mock_hub_ping.mock_calls) == 1
assert len(mock_hub_configuration_test.mock_calls) == 1
entity = hass.states.get("scene.raum_0_gute_nacht")
assert entity is not None
assert entity == snapshot
await async_setup_component(hass, "homeassistant", {})
await hass.services.async_call(
"homeassistant",
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: entity.entity_id},
blocking=True,
)
assert len(mock_scene_call.mock_calls) == 1