"""BleBox cover entities tests.""" import logging from unittest.mock import AsyncMock, PropertyMock import blebox_uniapi import pytest from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_POSITION, DEVICE_CLASS_DOOR, DEVICE_CLASS_GATE, DEVICE_CLASS_SHUTTER, STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, SUPPORT_STOP, ) from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER, SERVICE_SET_COVER_POSITION, SERVICE_STOP_COVER, STATE_UNKNOWN, ) from .conftest import async_setup_entity, mock_feature ALL_COVER_FIXTURES = ["gatecontroller", "shutterbox", "gatebox"] FIXTURES_SUPPORTING_STOP = ["gatecontroller", "shutterbox"] @pytest.fixture(name="shutterbox") def shutterbox_fixture(): """Return a shutterBox fixture.""" feature = mock_feature( "covers", blebox_uniapi.cover.Cover, unique_id="BleBox-shutterBox-2bee34e750b8-position", full_name="shutterBox-position", device_class="shutter", current=None, state=None, has_stop=True, is_slider=True, ) product = feature.product type(product).name = PropertyMock(return_value="My shutter") type(product).model = PropertyMock(return_value="shutterBox") return (feature, "cover.shutterbox_position") @pytest.fixture(name="gatebox") def gatebox_fixture(): """Return a gateBox fixture.""" feature = mock_feature( "covers", blebox_uniapi.cover.Cover, unique_id="BleBox-gateBox-1afe34db9437-position", device_class="gatebox", full_name="gateBox-position", current=None, state=None, has_stop=False, is_slider=False, ) product = feature.product type(product).name = PropertyMock(return_value="My gatebox") type(product).model = PropertyMock(return_value="gateBox") return (feature, "cover.gatebox_position") @pytest.fixture(name="gatecontroller") def gate_fixture(): """Return a gateController fixture.""" feature = mock_feature( "covers", blebox_uniapi.cover.Cover, unique_id="BleBox-gateController-2bee34e750b8-position", full_name="gateController-position", device_class="gate", current=None, state=None, has_stop=True, is_slider=True, ) product = feature.product type(product).name = PropertyMock(return_value="My gate controller") type(product).model = PropertyMock(return_value="gateController") return (feature, "cover.gatecontroller_position") async def test_init_gatecontroller(gatecontroller, hass, config): """Test gateController default state.""" _, entity_id = gatecontroller entry = await async_setup_entity(hass, config, entity_id) assert entry.unique_id == "BleBox-gateController-2bee34e750b8-position" state = hass.states.get(entity_id) assert state.name == "gateController-position" assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_GATE supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & SUPPORT_OPEN assert supported_features & SUPPORT_CLOSE assert supported_features & SUPPORT_STOP assert supported_features & SUPPORT_SET_POSITION assert ATTR_CURRENT_POSITION not in state.attributes assert state.state == STATE_UNKNOWN device_registry = await hass.helpers.device_registry.async_get_registry() device = device_registry.async_get(entry.device_id) assert device.name == "My gate controller" assert device.identifiers == {("blebox", "abcd0123ef5678")} assert device.manufacturer == "BleBox" assert device.model == "gateController" assert device.sw_version == "1.23" async def test_init_shutterbox(shutterbox, hass, config): """Test gateBox default state.""" _, entity_id = shutterbox entry = await async_setup_entity(hass, config, entity_id) assert entry.unique_id == "BleBox-shutterBox-2bee34e750b8-position" state = hass.states.get(entity_id) assert state.name == "shutterBox-position" assert entry.device_class == DEVICE_CLASS_SHUTTER supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & SUPPORT_OPEN assert supported_features & SUPPORT_CLOSE assert supported_features & SUPPORT_STOP assert supported_features & SUPPORT_SET_POSITION assert ATTR_CURRENT_POSITION not in state.attributes assert state.state == STATE_UNKNOWN device_registry = await hass.helpers.device_registry.async_get_registry() device = device_registry.async_get(entry.device_id) assert device.name == "My shutter" assert device.identifiers == {("blebox", "abcd0123ef5678")} assert device.manufacturer == "BleBox" assert device.model == "shutterBox" assert device.sw_version == "1.23" async def test_init_gatebox(gatebox, hass, config): """Test cover default state.""" _, entity_id = gatebox entry = await async_setup_entity(hass, config, entity_id) assert entry.unique_id == "BleBox-gateBox-1afe34db9437-position" state = hass.states.get(entity_id) assert state.name == "gateBox-position" assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_DOOR supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & SUPPORT_OPEN assert supported_features & SUPPORT_CLOSE # Not available during init since requires fetching state to detect assert not supported_features & SUPPORT_STOP assert not supported_features & SUPPORT_SET_POSITION assert ATTR_CURRENT_POSITION not in state.attributes assert state.state == STATE_UNKNOWN device_registry = await hass.helpers.device_registry.async_get_registry() device = device_registry.async_get(entry.device_id) assert device.name == "My gatebox" assert device.identifiers == {("blebox", "abcd0123ef5678")} assert device.manufacturer == "BleBox" assert device.model == "gateBox" assert device.sw_version == "1.23" @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_open(feature, hass, config): """Test cover opening.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 3 # manually stopped def open_gate(): feature_mock.state = 1 # opening feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_open = AsyncMock(side_effect=open_gate) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED feature_mock.async_update = AsyncMock() await hass.services.async_call( "cover", SERVICE_OPEN_COVER, {"entity_id": entity_id}, blocking=True, ) assert hass.states.get(entity_id).state == STATE_OPENING @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_close(feature, hass, config): """Test cover closing.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 4 # open def close(): feature_mock.state = 0 # closing feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_close = AsyncMock(side_effect=close) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_OPEN feature_mock.async_update = AsyncMock() await hass.services.async_call( "cover", SERVICE_CLOSE_COVER, {"entity_id": entity_id}, blocking=True ) assert hass.states.get(entity_id).state == STATE_CLOSING def opening_to_stop_feature_mock(feature_mock): """Return an mocked feature which can be updated and stopped.""" def initial_update(): feature_mock.state = 1 # opening def stop(): feature_mock.state = 2 # manually stopped feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_stop = AsyncMock(side_effect=stop) @pytest.mark.parametrize("feature", FIXTURES_SUPPORTING_STOP, indirect=["feature"]) async def test_stop(feature, hass, config): """Test cover stopping.""" feature_mock, entity_id = feature opening_to_stop_feature_mock(feature_mock) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_OPENING feature_mock.async_update = AsyncMock() await hass.services.async_call( "cover", SERVICE_STOP_COVER, {"entity_id": entity_id}, blocking=True ) assert hass.states.get(entity_id).state == STATE_OPEN @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_update(feature, hass, config): """Test cover updating.""" feature_mock, entity_id = feature def initial_update(): feature_mock.current = 29 # inverted feature_mock.state = 2 # manually stopped feature_mock.async_update = AsyncMock(side_effect=initial_update) await async_setup_entity(hass, config, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_CURRENT_POSITION] == 71 # 100 - 29 assert state.state == STATE_OPEN @pytest.mark.parametrize( "feature", ["gatecontroller", "shutterbox"], indirect=["feature"] ) async def test_set_position(feature, hass, config): """Test cover position setting.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 3 # closed def set_position(position): assert position == 99 # inverted feature_mock.state = 1 # opening # feature_mock.current = position feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_set_position = AsyncMock(side_effect=set_position) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED feature_mock.async_update = AsyncMock() await hass.services.async_call( "cover", SERVICE_SET_COVER_POSITION, {"entity_id": entity_id, ATTR_POSITION: 1}, blocking=True, ) # almost closed assert hass.states.get(entity_id).state == STATE_OPENING async def test_unknown_position(shutterbox, hass, config): """Test cover position setting.""" feature_mock, entity_id = shutterbox def initial_update(): feature_mock.state = 4 # opening feature_mock.current = -1 feature_mock.async_update = AsyncMock(side_effect=initial_update) await async_setup_entity(hass, config, entity_id) state = hass.states.get(entity_id) assert state.state == STATE_OPEN assert ATTR_CURRENT_POSITION not in state.attributes async def test_with_stop(gatebox, hass, config): """Test stop capability is available.""" feature_mock, entity_id = gatebox opening_to_stop_feature_mock(feature_mock) feature_mock.has_stop = True await async_setup_entity(hass, config, entity_id) state = hass.states.get(entity_id) supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & SUPPORT_STOP async def test_with_no_stop(gatebox, hass, config): """Test stop capability is not available.""" feature_mock, entity_id = gatebox opening_to_stop_feature_mock(feature_mock) feature_mock.has_stop = False await async_setup_entity(hass, config, entity_id) state = hass.states.get(entity_id) supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert not supported_features & SUPPORT_STOP @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_update_failure(feature, hass, config, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = feature feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) await async_setup_entity(hass, config, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_opening_state(feature, hass, config): """Test that entity properties work.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 1 # opening feature_mock.async_update = AsyncMock(side_effect=initial_update) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_OPENING @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_closing_state(feature, hass, config): """Test that entity properties work.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 0 # closing feature_mock.async_update = AsyncMock(side_effect=initial_update) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSING @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) async def test_closed_state(feature, hass, config): """Test that entity properties work.""" feature_mock, entity_id = feature def initial_update(): feature_mock.state = 3 # closed feature_mock.async_update = AsyncMock(side_effect=initial_update) await async_setup_entity(hass, config, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED