diff --git a/tests/components/velbus/conftest.py b/tests/components/velbus/conftest.py index 93a22c362f634dc4e58cd63847eb7c46a2fe29be..95f691b34f87bd703a8dcbc20d13f8edd26c154b 100644 --- a/tests/components/velbus/conftest.py +++ b/tests/components/velbus/conftest.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from velbusaio.channels import ( + Blind, Button, ButtonCounter, Dimmer, @@ -38,6 +39,8 @@ def mock_controller( mock_dimmer: AsyncMock, mock_module_no_subdevices: AsyncMock, mock_module_subdevices: AsyncMock, + mock_cover: AsyncMock, + mock_cover_no_position: AsyncMock, ) -> Generator[AsyncMock]: """Mock a successful velbus controller.""" with ( @@ -61,6 +64,7 @@ def mock_controller( ] cont.get_all_light.return_value = [mock_dimmer] cont.get_all_led.return_value = [mock_button] + cont.get_all_cover.return_value = [mock_cover, mock_cover_no_position] cont.get_modules.return_value = { 1: mock_module_no_subdevices, 2: mock_module_no_subdevices, @@ -251,6 +255,48 @@ def mock_dimmer() -> AsyncMock: return channel +@pytest.fixture +def mock_cover() -> AsyncMock: + """Mock a successful velbus channel.""" + channel = AsyncMock(spec=Blind) + channel.get_categories.return_value = ["cover"] + channel.get_name.return_value = "CoverName" + channel.get_module_address.return_value = 201 + channel.get_channel_number.return_value = 2 + channel.get_module_type_name.return_value = "VMB2BLE" + channel.get_full_name.return_value = "Full cover name" + channel.get_module_sw_version.return_value = "1.0.1" + channel.get_module_serial.return_value = "1234" + channel.support_position.return_value = True + channel.get_position.return_value = 50 + channel.is_closed.return_value = False + channel.is_open.return_value = True + channel.is_opening.return_value = False + channel.is_closing.return_value = False + return channel + + +@pytest.fixture +def mock_cover_no_position() -> AsyncMock: + """Mock a successful velbus channel.""" + channel = AsyncMock(spec=Blind) + channel.get_categories.return_value = ["cover"] + channel.get_name.return_value = "CoverNameNoPos" + channel.get_module_address.return_value = 200 + channel.get_channel_number.return_value = 1 + channel.get_module_type_name.return_value = "VMB2BLE" + channel.get_full_name.return_value = "Full cover name no position" + channel.get_module_sw_version.return_value = "1.0.1" + channel.get_module_serial.return_value = "12345" + channel.support_position.return_value = False + channel.get_position.return_value = None + channel.is_closed.return_value = False + channel.is_open.return_value = True + channel.is_opening.return_value = True + channel.is_closing.return_value = True + return channel + + @pytest.fixture(name="config_entry") async def mock_config_entry( hass: HomeAssistant, diff --git a/tests/components/velbus/snapshots/test_cover.ambr b/tests/components/velbus/snapshots/test_cover.ambr new file mode 100644 index 0000000000000000000000000000000000000000..eb41839078d1ade9be8642c9617bae7791333515 --- /dev/null +++ b/tests/components/velbus/snapshots/test_cover.ambr @@ -0,0 +1,97 @@ +# serializer version: 1 +# name: test_entities[cover.covername-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': <ANY>, + 'device_class': None, + 'device_id': <ANY>, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.covername', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': <ANY>, + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'CoverName', + 'platform': 'velbus', + 'previous_unique_id': None, + 'supported_features': <CoverEntityFeature: 15>, + 'translation_key': None, + 'unique_id': '1234-2', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[cover.covername-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_position': 50, + 'friendly_name': 'CoverName', + 'supported_features': <CoverEntityFeature: 15>, + }), + 'context': <ANY>, + 'entity_id': 'cover.covername', + 'last_changed': <ANY>, + 'last_reported': <ANY>, + 'last_updated': <ANY>, + 'state': 'open', + }) +# --- +# name: test_entities[cover.covernamenopos-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': <ANY>, + 'device_class': None, + 'device_id': <ANY>, + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.covernamenopos', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': <ANY>, + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'CoverNameNoPos', + 'platform': 'velbus', + 'previous_unique_id': None, + 'supported_features': <CoverEntityFeature: 11>, + 'translation_key': None, + 'unique_id': '12345-1', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[cover.covernamenopos-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'assumed_state': True, + 'friendly_name': 'CoverNameNoPos', + 'supported_features': <CoverEntityFeature: 11>, + }), + 'context': <ANY>, + 'entity_id': 'cover.covernamenopos', + 'last_changed': <ANY>, + 'last_reported': <ANY>, + 'last_updated': <ANY>, + 'state': 'opening', + }) +# --- diff --git a/tests/components/velbus/test_cover.py b/tests/components/velbus/test_cover.py new file mode 100644 index 0000000000000000000000000000000000000000..fe3fbbe1594aee7921ff399fee3642b577304530 --- /dev/null +++ b/tests/components/velbus/test_cover.py @@ -0,0 +1,90 @@ +"""Velbus cover platform tests.""" + +from unittest.mock import AsyncMock, patch + +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.cover import ATTR_POSITION, DOMAIN as COVER_DOMAIN +from homeassistant.const import ( + ATTR_ENTITY_ID, + SERVICE_CLOSE_COVER, + SERVICE_OPEN_COVER, + SERVICE_SET_COVER_POSITION, + SERVICE_STOP_COVER, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import init_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + with patch("homeassistant.components.velbus.PLATFORMS", [Platform.COVER]): + await init_integration(hass, config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) + + +@pytest.mark.parametrize( + ("entity_id", "entity_num"), + [ + ("cover.covername", 0), + ("cover.covernamenopos", 1), + ], +) +async def test_actions( + hass: HomeAssistant, + config_entry: MockConfigEntry, + entity_id: str, + entity_num: int, +) -> None: + """Test the cover actions.""" + await init_integration(hass, config_entry) + entity = config_entry.runtime_data.controller.get_all_cover()[entity_num] + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_CLOSE_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + entity.close.assert_called_once() + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + entity.open.assert_called_once() + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_STOP_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + entity.stop.assert_called_once() + + +async def test_position( + hass: HomeAssistant, + config_entry: MockConfigEntry, + mock_cover: AsyncMock, +) -> None: + """Test the set_postion over action.""" + await init_integration(hass, config_entry) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.covername", ATTR_POSITION: 25}, + blocking=True, + ) + mock_cover.set_position.assert_called_once_with(75)