diff --git a/tests/components/homee/__init__.py b/tests/components/homee/__init__.py index 95fc6099269..a5f8ae00d1e 100644 --- a/tests/components/homee/__init__.py +++ b/tests/components/homee/__init__.py @@ -1,8 +1,14 @@ """Tests for the homee component.""" +from typing import Any +from unittest.mock import AsyncMock + +from pyHomee.model import HomeeAttribute, HomeeNode + +from homeassistant.components.homee.const import DOMAIN from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_json_object_fixture async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: @@ -11,3 +17,35 @@ async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() + + +def build_mock_node(file: str) -> AsyncMock: + """Build a mocked Homee node from a json representation.""" + json_node = load_json_object_fixture(file, DOMAIN) + mock_node = AsyncMock(spec=HomeeNode) + + def get_attributes(attributes: list[Any]) -> list[AsyncMock]: + mock_attributes: list[AsyncMock] = [] + for attribute in attributes: + att = AsyncMock(spec=HomeeAttribute) + for key, value in attribute.items(): + setattr(att, key, value) + att.is_reversed = False + att.get_value = ( + lambda att=att: att.data if att.unit == "text" else att.current_value + ) + mock_attributes.append(att) + return mock_attributes + + for key, value in json_node.items(): + if key != "attributes": + setattr(mock_node, key, value) + + mock_node.attributes = get_attributes(json_node["attributes"]) + + def attribute_by_type(type, instance=0) -> HomeeAttribute | None: + return {attr.type: attr for attr in mock_node.attributes}.get(type) + + mock_node.get_attribute_by_type = attribute_by_type + + return mock_node diff --git a/tests/components/homee/conftest.py b/tests/components/homee/conftest.py index fb94ba0bbcc..5a3234e896b 100644 --- a/tests/components/homee/conftest.py +++ b/tests/components/homee/conftest.py @@ -61,6 +61,8 @@ def mock_homee() -> Generator[AsyncMock]: homee.settings = MagicMock() homee.settings.uid = HOMEE_ID homee.settings.homee_name = HOMEE_NAME + homee.settings.version = "1.2.3" + homee.settings.mac_address = "00:05:55:11:ee:cc" homee.reconnect_interval = 10 homee.connected = True diff --git a/tests/components/homee/fixtures/cover3.json b/tests/components/homee/fixtures/cover3.json deleted file mode 100644 index 0d3d5ea57e2..00000000000 --- a/tests/components/homee/fixtures/cover3.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "id": 3, - "name": "Test%20Cover", - "profile": 2002, - "image": "default", - "favorite": 0, - "order": 4, - "protocol": 23, - "routing": 0, - "state": 1, - "state_changed": 1687175681, - "added": 1672086680, - "history": 1, - "cube_type": 14, - "note": "TestCoverDevice", - "services": 7, - "phonetic_name": "", - "owner": 2, - "security": 0, - "attributes": [ - { - "id": 1, - "node_id": 3, - "instance": 0, - "minimum": 0, - "maximum": 4, - "current_value": 3.0, - "target_value": 0.0, - "last_value": 1.0, - "unit": "n%2Fa", - "step_value": 1.0, - "editable": 1, - "type": 135, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "can_observe": [300], - "observes": [75], - "automations": ["toggle"] - } - }, - { - "id": 2, - "node_id": 3, - "instance": 0, - "minimum": 0, - "maximum": 100, - "current_value": 75.0, - "target_value": 0.0, - "last_value": 100.0, - "unit": "%25", - "step_value": 0.5, - "editable": 1, - "type": 15, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "automations": ["step"], - "history": { - "day": 35, - "week": 5, - "month": 1 - } - } - }, - { - "id": 3, - "node_id": 3, - "instance": 0, - "minimum": -45, - "maximum": 90, - "current_value": 56.0, - "target_value": 56.0, - "last_value": 0.0, - "unit": "%C2%B0", - "step_value": 1.0, - "editable": 1, - "type": 113, - "state": 1, - "last_changed": 1678284920, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "automations": ["step"] - } - } - ] -} diff --git a/tests/components/homee/fixtures/cover4.json b/tests/components/homee/fixtures/cover4.json deleted file mode 100644 index a3de555794a..00000000000 --- a/tests/components/homee/fixtures/cover4.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "id": 3, - "name": "Test%20Cover", - "profile": 2002, - "image": "default", - "favorite": 0, - "order": 4, - "protocol": 23, - "routing": 0, - "state": 1, - "state_changed": 1687175681, - "added": 1672086680, - "history": 1, - "cube_type": 14, - "note": "TestCoverDevice", - "services": 7, - "phonetic_name": "", - "owner": 2, - "security": 0, - "attributes": [ - { - "id": 1, - "node_id": 3, - "instance": 0, - "minimum": 0, - "maximum": 4, - "current_value": 4.0, - "target_value": 1.0, - "last_value": 0.0, - "unit": "n%2Fa", - "step_value": 1.0, - "editable": 1, - "type": 135, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "can_observe": [300], - "observes": [75], - "automations": ["toggle"] - } - }, - { - "id": 2, - "node_id": 3, - "instance": 0, - "minimum": 0, - "maximum": 100, - "current_value": 25.0, - "target_value": 100.0, - "last_value": 0.0, - "unit": "%25", - "step_value": 0.5, - "editable": 1, - "type": 15, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "automations": ["step"], - "history": { - "day": 35, - "week": 5, - "month": 1 - } - } - }, - { - "id": 3, - "node_id": 3, - "instance": 0, - "minimum": -45, - "maximum": 90, - "current_value": -11.0, - "target_value": 0.0, - "last_value": -45.0, - "unit": "%C2%B0", - "step_value": 1.0, - "editable": 1, - "type": 113, - "state": 1, - "last_changed": 1678284920, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "automations": ["step"] - } - } - ] -} diff --git a/tests/components/homee/fixtures/cover1.json b/tests/components/homee/fixtures/cover_with_position_slats.json similarity index 95% rename from tests/components/homee/fixtures/cover1.json rename to tests/components/homee/fixtures/cover_with_position_slats.json index 8fedfb19d4f..8fd0d6f44fe 100644 --- a/tests/components/homee/fixtures/cover1.json +++ b/tests/components/homee/fixtures/cover_with_position_slats.json @@ -1,6 +1,6 @@ { "id": 3, - "name": "Test%20Cover", + "name": "Test Cover", "profile": 2002, "image": "default", "favorite": 0, @@ -27,7 +27,7 @@ "current_value": 1.0, "target_value": 1.0, "last_value": 4.0, - "unit": "n%2Fa", + "unit": "n/a", "step_value": 1.0, "editable": 1, "type": 135, @@ -53,7 +53,7 @@ "current_value": 0.0, "target_value": 0.0, "last_value": 0.0, - "unit": "%25", + "unit": "%", "step_value": 0.5, "editable": 1, "type": 15, @@ -82,7 +82,7 @@ "current_value": -45.0, "target_value": 0.0, "last_value": -45.0, - "unit": "%C2%B0", + "unit": "°", "step_value": 1.0, "editable": 1, "type": 113, diff --git a/tests/components/homee/fixtures/cover2.json b/tests/components/homee/fixtures/cover_with_slats_position.json similarity index 50% rename from tests/components/homee/fixtures/cover2.json rename to tests/components/homee/fixtures/cover_with_slats_position.json index b53c3d49b62..4b6eb466a85 100644 --- a/tests/components/homee/fixtures/cover2.json +++ b/tests/components/homee/fixtures/cover_with_slats_position.json @@ -1,19 +1,19 @@ { "id": 1, - "name": "Test%20Cover", + "name": "Test Slats", "profile": 2002, "image": "default", "favorite": 0, - "order": 4, + "order": 1, "protocol": 23, "routing": 0, "state": 1, - "state_changed": 1687175681, - "added": 1672086680, + "state_changed": 1676901608, + "added": 1672148537, "history": 1, "cube_type": 14, - "note": "TestCoverDevice", - "services": 7, + "note": "", + "services": 70, "phonetic_name": "", "owner": 2, "security": 0, @@ -22,67 +22,12 @@ "id": 1, "node_id": 1, "instance": 0, - "minimum": 0, - "maximum": 4, - "current_value": 1.0, - "target_value": 1.0, - "last_value": 0.0, - "unit": "n%2Fa", - "step_value": 1.0, - "editable": 1, - "type": 135, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "can_observe": [300], - "observes": [75], - "automations": ["toggle"] - } - }, - { - "id": 2, - "node_id": 1, - "instance": 0, - "minimum": 0, - "maximum": 100, - "current_value": 100.0, - "target_value": 0.0, - "last_value": 0.0, - "unit": "%25", - "step_value": 0.5, - "editable": 1, - "type": 15, - "state": 1, - "last_changed": 1687175680, - "changed_by": 1, - "changed_by_id": 0, - "based_on": 1, - "data": "", - "name": "", - "options": { - "automations": ["step"], - "history": { - "day": 35, - "week": 5, - "month": 1 - } - } - }, - { - "id": 3, - "node_id": 1, - "instance": 0, "minimum": -45, "maximum": 90, - "current_value": 90.0, - "target_value": 0.0, - "last_value": -45.0, - "unit": "%C2%B0", + "current_value": 1.0, + "target_value": 1.0, + "last_value": -21.0, + "unit": "°", "step_value": 1.0, "editable": 1, "type": 113, @@ -96,6 +41,31 @@ "options": { "automations": ["step"] } + }, + { + "id": 2, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 2, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 1.0, + "unit": "n/a", + "step_value": 1.0, + "editable": 1, + "type": 337, + "state": 1, + "last_changed": 1678284911, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "can_observe": [300], + "observes": [72] + } } ] } diff --git a/tests/components/homee/fixtures/cover_without_position.json b/tests/components/homee/fixtures/cover_without_position.json new file mode 100644 index 00000000000..e2bc6c7a38d --- /dev/null +++ b/tests/components/homee/fixtures/cover_without_position.json @@ -0,0 +1,48 @@ +{ + "id": 3, + "name": "Test Cover", + "profile": 2002, + "image": "default", + "favorite": 0, + "order": 4, + "protocol": 23, + "routing": 0, + "state": 1, + "state_changed": 1687175681, + "added": 1672086680, + "history": 1, + "cube_type": 14, + "note": "TestCoverDevice", + "services": 7, + "phonetic_name": "", + "owner": 2, + "security": 0, + "attributes": [ + { + "id": 1, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 4, + "current_value": 1.0, + "target_value": 1.0, + "last_value": 4.0, + "unit": "n/a", + "step_value": 1.0, + "editable": 1, + "type": 135, + "state": 1, + "last_changed": 1687175680, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "can_observe": [300], + "observes": [75], + "automations": ["toggle"] + } + } + ] +} diff --git a/tests/components/homee/test_cover.py b/tests/components/homee/test_cover.py index a7feaa10b66..d52f3fa3164 100644 --- a/tests/components/homee/test_cover.py +++ b/tests/components/homee/test_cover.py @@ -1,97 +1,38 @@ """Test homee covers.""" -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import MagicMock -from pyHomee import HomeeNode - -from homeassistant.components.cover import DOMAIN as COVER_DOMAIN, CoverState -from homeassistant.components.homee.const import DOMAIN -from homeassistant.const import ATTR_ENTITY_ID, SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER +from homeassistant.components.cover import ( + ATTR_POSITION, + ATTR_TILT_POSITION, + DOMAIN as COVER_DOMAIN, + CoverEntityFeature, + CoverState, +) +from homeassistant.const import ( + ATTR_ENTITY_ID, + SERVICE_CLOSE_COVER, + SERVICE_CLOSE_COVER_TILT, + SERVICE_OPEN_COVER, + SERVICE_OPEN_COVER_TILT, + SERVICE_SET_COVER_POSITION, + SERVICE_SET_COVER_TILT_POSITION, + SERVICE_STOP_COVER, +) from homeassistant.core import HomeAssistant -from . import setup_integration +from . import build_mock_node, setup_integration -from tests.common import MockConfigEntry, load_json_object_fixture +from tests.common import MockConfigEntry -async def test_cover_open( - hass: HomeAssistant, mock_homee: AsyncMock, mock_config_entry: MockConfigEntry -) -> None: - """Test an open cover.""" - # Cover open, tilt open. - cover_json = load_json_object_fixture("cover1.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] - - await setup_integration(hass, mock_config_entry) - - assert hass.states.get("cover.test_cover").state == CoverState.OPEN - - attributes = hass.states.get("cover.test_cover").attributes - assert attributes.get("supported_features") == 143 - assert attributes.get("current_position") == 100 - assert attributes.get("current_tilt_position") == 100 - - -async def test_cover_closed( - hass: HomeAssistant, mock_homee: MagicMock, mock_config_entry: MockConfigEntry -) -> None: - """Test a closed cover.""" - # Cover closed, tilt closed. - cover_json = load_json_object_fixture("cover2.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] - - await setup_integration(hass, mock_config_entry) - - assert hass.states.get("cover.test_cover").state == CoverState.CLOSED - attributes = hass.states.get("cover.test_cover").attributes - assert attributes.get("current_position") == 0 - assert attributes.get("current_tilt_position") == 0 - - -async def test_cover_opening( - hass: HomeAssistant, mock_homee: MagicMock, mock_config_entry: MockConfigEntry -) -> None: - """Test an opening cover.""" - # opening, 75% homee / 25% HA - cover_json = load_json_object_fixture("cover3.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] - - await setup_integration(hass, mock_config_entry) - - assert hass.states.get("cover.test_cover").state == CoverState.OPENING - attributes = hass.states.get("cover.test_cover").attributes - assert attributes.get("current_position") == 25 - assert attributes.get("current_tilt_position") == 25 - - -async def test_cover_closing( - hass: HomeAssistant, mock_homee: MagicMock, mock_config_entry: MockConfigEntry -) -> None: - """Test a closing cover.""" - # closing, 25% homee / 75% HA - cover_json = load_json_object_fixture("cover4.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] - - await setup_integration(hass, mock_config_entry) - - assert hass.states.get("cover.test_cover").state == CoverState.CLOSING - attributes = hass.states.get("cover.test_cover").attributes - assert attributes.get("current_position") == 75 - assert attributes.get("current_tilt_position") == 74 - - -async def test_open_cover( - hass: HomeAssistant, mock_homee: MagicMock, mock_config_entry: MockConfigEntry +async def test_open_close_stop_cover( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, ) -> None: """Test opening the cover.""" - # Cover closed, tilt closed. - cover_json = load_json_object_fixture("cover2.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] + mock_homee.nodes = [build_mock_node("cover_with_position_slats.json")] await setup_integration(hass, mock_config_entry) @@ -101,24 +42,214 @@ async def test_open_cover( {ATTR_ENTITY_ID: "cover.test_cover"}, blocking=True, ) - mock_homee.set_value.assert_called_once_with(cover_node.id, 1, 0) - - -async def test_close_cover( - hass: HomeAssistant, mock_homee: MagicMock, mock_config_entry: MockConfigEntry -) -> None: - """Test opening the cover.""" - # Cover open, tilt open. - cover_json = load_json_object_fixture("cover1.json", DOMAIN) - cover_node = HomeeNode(cover_json) - mock_homee.nodes = [cover_node] - - await setup_integration(hass, mock_config_entry) - await hass.services.async_call( COVER_DOMAIN, SERVICE_CLOSE_COVER, {ATTR_ENTITY_ID: "cover.test_cover"}, blocking=True, ) - mock_homee.set_value.assert_called_once_with(cover_node.id, 1, 1) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_STOP_COVER, + {ATTR_ENTITY_ID: "cover.test_cover"}, + blocking=True, + ) + + calls = mock_homee.set_value.call_args_list + for index, call in enumerate(calls): + assert call[0] == (mock_homee.nodes[0].id, 1, index) + + +async def test_set_cover_position( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test setting the cover position.""" + mock_homee.nodes = [build_mock_node("cover_with_position_slats.json")] + + await setup_integration(hass, mock_config_entry) + + # Slats have a range of -45 to 90. + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_POSITION: 100}, + blocking=True, + ) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_POSITION: 0}, + blocking=True, + ) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_POSITION: 50}, + blocking=True, + ) + + calls = mock_homee.set_value.call_args_list + positions = [0, 100, 50] + for call in calls: + assert call[0] == (1, 2, positions.pop(0)) + + +async def test_close_open_slats( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test closing and opening slats.""" + mock_homee.nodes = [build_mock_node("cover_with_slats_position.json")] + + await setup_integration(hass, mock_config_entry) + + attributes = hass.states.get("cover.test_slats").attributes + assert attributes.get("supported_features") == ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.SET_TILT_POSITION + ) + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_CLOSE_COVER_TILT, + {ATTR_ENTITY_ID: "cover.test_slats"}, + blocking=True, + ) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER_TILT, + {ATTR_ENTITY_ID: "cover.test_slats"}, + blocking=True, + ) + + calls = mock_homee.set_value.call_args_list + for index, call in enumerate(calls, start=1): + assert call[0] == (mock_homee.nodes[0].id, 2, index) + + +async def test_set_slat_position( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test setting slats position.""" + mock_homee.nodes = [build_mock_node("cover_with_slats_position.json")] + + await setup_integration(hass, mock_config_entry) + + # Slats have a range of -45 to 90 on this device. + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_TILT_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_TILT_POSITION: 100}, + blocking=True, + ) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_TILT_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_TILT_POSITION: 0}, + blocking=True, + ) + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_TILT_POSITION, + {ATTR_ENTITY_ID: "cover.test_slats", ATTR_TILT_POSITION: 50}, + blocking=True, + ) + + calls = mock_homee.set_value.call_args_list + positions = [-45, 90, 22.5] + for call in calls: + assert call[0] == (1, 1, positions.pop(0)) + + +async def test_cover_positions( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test an open cover.""" + # Cover open, tilt open. + # mock_homee.nodes = [cover] + mock_homee.nodes = [build_mock_node("cover_with_position_slats.json")] + cover = mock_homee.nodes[0] + + await setup_integration(hass, mock_config_entry) + + assert hass.states.get("cover.test_cover").state == CoverState.OPEN + + attributes = hass.states.get("cover.test_cover").attributes + assert attributes.get("supported_features") == ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + | CoverEntityFeature.STOP + | CoverEntityFeature.SET_TILT_POSITION + ) + assert attributes.get("current_position") == 100 + assert attributes.get("current_tilt_position") == 100 + + cover.attributes[0].current_value = 1 + cover.attributes[1].current_value = 100 + cover.attributes[2].current_value = 90 + cover.add_on_changed_listener.call_args_list[0][0][0](cover) + await hass.async_block_till_done() + + attributes = hass.states.get("cover.test_cover").attributes + assert attributes.get("current_position") == 0 + assert attributes.get("current_tilt_position") == 0 + assert hass.states.get("cover.test_cover").state == CoverState.CLOSED + + cover.attributes[0].current_value = 3 + cover.attributes[1].current_value = 75 + cover.attributes[2].current_value = 56 + cover.add_on_changed_listener.call_args_list[0][0][0](cover) + await hass.async_block_till_done() + + assert hass.states.get("cover.test_cover").state == CoverState.OPENING + attributes = hass.states.get("cover.test_cover").attributes + assert attributes.get("current_position") == 25 + assert attributes.get("current_tilt_position") == 25 + + cover.attributes[0].current_value = 4 + cover.attributes[1].current_value = 25 + cover.attributes[2].current_value = -11 + cover.add_on_changed_listener.call_args_list[0][0][0](cover) + await hass.async_block_till_done() + + assert hass.states.get("cover.test_cover").state == CoverState.CLOSING + attributes = hass.states.get("cover.test_cover").attributes + assert attributes.get("current_position") == 75 + assert attributes.get("current_tilt_position") == 74 + + +async def test_reversed_cover( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test a cover with inverted UP_DOWN attribute without position.""" + mock_homee.nodes = [build_mock_node("cover_without_position.json")] + cover = mock_homee.nodes[0] + + await setup_integration(hass, mock_config_entry) + + cover.attributes[0].is_reversed = True + cover.add_on_changed_listener.call_args_list[0][0][0](cover) + await hass.async_block_till_done() + + attributes = hass.states.get("cover.test_cover").attributes + assert attributes.get("supported_features") == ( + CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP + ) + assert hass.states.get("cover.test_cover").state == CoverState.OPEN + + cover.attributes[0].current_value = 0 + cover.add_on_changed_listener.call_args_list[0][0][0](cover) + await hass.async_block_till_done() + + assert hass.states.get("cover.test_cover").state == CoverState.CLOSED