"""Test the Z-Wave JS cover platform."""

import copy
import logging
from typing import Any
from unittest.mock import MagicMock

import pytest
from zwave_js_server.const import (
    CURRENT_STATE_PROPERTY,
    CURRENT_VALUE_PROPERTY,
    CommandClass,
    SetValueStatus,
)
from zwave_js_server.event import Event
from zwave_js_server.model.node import Node

from homeassistant.components.cover import (
    ATTR_CURRENT_POSITION,
    ATTR_CURRENT_TILT_POSITION,
    ATTR_POSITION,
    ATTR_TILT_POSITION,
    DOMAIN as COVER_DOMAIN,
    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,
    SERVICE_STOP_COVER_TILT,
    CoverDeviceClass,
    CoverEntityFeature,
    CoverState,
)
from homeassistant.components.zwave_js.const import LOGGER
from homeassistant.components.zwave_js.helpers import ZwaveValueMatcher
from homeassistant.const import (
    ATTR_DEVICE_CLASS,
    ATTR_ENTITY_ID,
    ATTR_SUPPORTED_FEATURES,
    STATE_UNKNOWN,
    Platform,
)
from homeassistant.core import HomeAssistant

from .common import replace_value_of_zwave_value

from tests.common import MockConfigEntry

WINDOW_COVER_ENTITY = "cover.zws_12"
GDC_COVER_ENTITY = "cover.aeon_labs_garage_door_controller_gen5"
BLIND_COVER_ENTITY = "cover.window_blind_controller"
SHUTTER_COVER_ENTITY = "cover.flush_shutter"
AEOTEC_SHUTTER_COVER_ENTITY = "cover.nano_shutter_v_3"
FIBARO_FGR_222_SHUTTER_COVER_ENTITY = "cover.fgr_222_test_cover"
FIBARO_FGR_223_SHUTTER_COVER_ENTITY = "cover.fgr_223_test_cover"
SHELLY_WAVE_SHUTTER_COVER_ENTITY = "cover.shelly_fw_14_2_0_test_cover"
LOGGER.setLevel(logging.DEBUG)


@pytest.fixture
def platforms() -> list[str]:
    """Fixture to specify platforms to test."""
    return [Platform.COVER]


@pytest.fixture(name="window_covering_outbound_bottom_no_position")
def window_covering_outbound_bottom_no_position_fixture(
    client: MagicMock,
    window_covering_outbound_bottom_state: dict[str, Any],
) -> Node:
    """Load a Window Covering node that does not support setting a position."""
    node_state = copy.deepcopy(window_covering_outbound_bottom_state)
    for value in node_state["values"]:
        if value.get("commandClass") != CommandClass.WINDOW_COVERING:
            continue
        if value.get("propertyKey") != 13:
            continue
        value["propertyKey"] = 12
        value["propertyKeyName"] = "Outbound Bottom (no position)"
        if "metadata" in value and "ccSpecific" in value["metadata"]:
            value["metadata"]["ccSpecific"]["parameter"] = 12
    node = Node(client, node_state)
    client.driver.controller.nodes[node.node_id] = node
    return node


async def test_window_cover(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test the cover entity."""
    node = chain_actuator_zws12
    state = hass.states.get(WINDOW_COVER_ENTITY)

    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.WINDOW

    assert state.state == CoverState.CLOSED
    assert state.attributes[ATTR_CURRENT_POSITION] == 0

    # Test setting position
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_POSITION,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY, ATTR_POSITION: 50},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 6
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"] == 50

    client.async_send_command.reset_mock()

    # Test setting position
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_POSITION,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY, ATTR_POSITION: 0},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 6
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    # Test opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 6
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"]

    client.async_send_command.reset_mock()

    # Test stop after opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    open_args = client.async_send_command.call_args_list[0][0][0]
    assert open_args["command"] == "node.set_value"
    assert open_args["nodeId"] == 6
    assert open_args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "Open",
    }
    assert not open_args["value"]

    # Test position update from value updated event
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 6,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)
    client.async_send_command.reset_mock()

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # Test closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 6
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    # Test stop after closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    open_args = client.async_send_command.call_args_list[0][0][0]
    assert open_args["command"] == "node.set_value"
    assert open_args["nodeId"] == 6
    assert open_args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "Open",
    }
    assert not open_args["value"]

    client.async_send_command.reset_mock()

    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 6,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 0,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED


async def test_fibaro_fgr222_shutter_cover(
    hass: HomeAssistant,
    client: MagicMock,
    fibaro_fgr222_shutter: Node,
    integration: MockConfigEntry,
) -> None:
    """Test tilt function of the Fibaro Shutter devices."""
    state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER

    assert state.state == CoverState.OPEN
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 0

    # Test opening tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER_TILT,
        {ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 42
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 145,
        "property": "fibaro",
        "propertyKey": "venetianBlindsTilt",
    }
    assert args["value"] == 99

    client.async_send_command.reset_mock()

    # Test closing tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER_TILT,
        {ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 42
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 145,
        "property": "fibaro",
        "propertyKey": "venetianBlindsTilt",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    # Test setting tilt position
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_TILT_POSITION,
        {ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 42
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 145,
        "property": "fibaro",
        "propertyKey": "venetianBlindsTilt",
    }
    assert args["value"] == 12

    # Test some tilt
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 42,
            "args": {
                "commandClassName": "Manufacturer Proprietary",
                "commandClass": 145,
                "endpoint": 0,
                "property": "fibaro",
                "propertyKey": "venetianBlindsTilt",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "fibaro",
                "propertyKeyName": "venetianBlindsTilt",
            },
        },
    )
    fibaro_fgr222_shutter.receive_event(event)
    state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100


async def test_fibaro_fgr223_shutter_cover(
    hass: HomeAssistant,
    client: MagicMock,
    fibaro_fgr223_shutter: Node,
    integration: MockConfigEntry,
) -> None:
    """Test tilt function of the Fibaro Shutter devices."""
    state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER

    assert state.state == CoverState.OPEN
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 0

    # Test opening tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER_TILT,
        {ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 10
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 99

    client.async_send_command.reset_mock()
    # Test closing tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER_TILT,
        {ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 10
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()
    # Test setting tilt position
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_TILT_POSITION,
        {ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 10
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 12

    # Test some tilt
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 10,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 2,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    fibaro_fgr223_shutter.receive_event(event)
    state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100


async def test_shelly_wave_shutter_cover_with_tilt(
    hass: HomeAssistant,
    client: MagicMock,
    qubino_shutter_firmware_14_2_0: Node,
    integration: MockConfigEntry,
) -> None:
    """Test tilt function of the Shelly Wave Shutter with firmware 14.2.0.

    When parameter 71 is set to 1 (Venetian mode), endpoint 2 controls the tilt.
    """
    state = hass.states.get(SHELLY_WAVE_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER

    assert state.state == CoverState.CLOSED
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 0

    # Test opening tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER_TILT,
        {ATTR_ENTITY_ID: SHELLY_WAVE_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert client.async_send_command.call_count == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 5
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 99

    client.async_send_command.reset_mock()

    # Test closing tilts
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER_TILT,
        {ATTR_ENTITY_ID: SHELLY_WAVE_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert client.async_send_command.call_count == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 5
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    # Test setting tilt position
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_TILT_POSITION,
        {ATTR_ENTITY_ID: SHELLY_WAVE_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
        blocking=True,
    )

    assert client.async_send_command.call_count == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 5
    assert args["valueId"] == {
        "endpoint": 2,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 12

    # Test some tilt
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 5,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 2,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    qubino_shutter_firmware_14_2_0.receive_event(event)
    state = hass.states.get(SHELLY_WAVE_SHUTTER_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100


async def test_aeotec_nano_shutter_cover(
    hass: HomeAssistant,
    client: MagicMock,
    aeotec_nano_shutter: Node,
    integration: MockConfigEntry,
) -> None:
    """Test Aeotec Nano Shutter cover entity stop command logic."""
    node = aeotec_nano_shutter
    state = hass.states.get(AEOTEC_SHUTTER_COVER_ENTITY)

    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.WINDOW

    assert state.state == CoverState.CLOSED
    assert state.attributes[ATTR_CURRENT_POSITION] == 0

    # Test opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: AEOTEC_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 3
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"]

    client.async_send_command.reset_mock()

    # Test stop after opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: AEOTEC_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    open_args = client.async_send_command.call_args_list[0][0][0]
    assert open_args["command"] == "node.set_value"
    assert open_args["nodeId"] == 3
    assert open_args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "On",
    }
    assert not open_args["value"]

    # Test position update from value updated event
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 6,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    client.async_send_command.reset_mock()

    state = hass.states.get(AEOTEC_SHUTTER_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # Test closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: AEOTEC_SHUTTER_COVER_ENTITY},
        blocking=True,
    )
    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 3
    assert args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    # Test stop after closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: AEOTEC_SHUTTER_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    open_args = client.async_send_command.call_args_list[0][0][0]
    assert open_args["command"] == "node.set_value"
    assert open_args["nodeId"] == 3
    assert open_args["valueId"] == {
        "commandClass": 38,
        "endpoint": 0,
        "property": "On",
    }
    assert not open_args["value"]


async def test_blind_cover(
    hass: HomeAssistant,
    client: MagicMock,
    iblinds_v2: Node,
    integration: MockConfigEntry,
) -> None:
    """Test a blind cover entity."""
    state = hass.states.get(BLIND_COVER_ENTITY)

    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.BLIND


async def test_shutter_cover(
    hass: HomeAssistant,
    client: MagicMock,
    qubino_shutter: Node,
    integration: MockConfigEntry,
) -> None:
    """Test a shutter cover entity."""
    state = hass.states.get(SHUTTER_COVER_ENTITY)

    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER


async def test_motor_barrier_cover(
    hass: HomeAssistant,
    client: MagicMock,
    gdc_zw062: Node,
    integration: MockConfigEntry,
) -> None:
    """Test the cover entity."""
    node = gdc_zw062

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.GARAGE

    assert state.state == CoverState.CLOSED

    # Test open
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: GDC_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 12
    assert args["value"] == 255
    assert args["valueId"] == {
        "commandClass": 102,
        "endpoint": 0,
        "property": "targetState",
    }

    # state doesn't change until currentState value update is received
    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    client.async_send_command.reset_mock()

    # Test close
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: GDC_COVER_ENTITY},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 12
    assert args["value"] == 0
    assert args["valueId"] == {
        "commandClass": 102,
        "endpoint": 0,
        "property": "targetState",
    }

    # state doesn't change until currentState value update is received
    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    client.async_send_command.reset_mock()

    # Barrier sends an opening state
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 12,
            "args": {
                "commandClassName": "Barrier Operator",
                "commandClass": 102,
                "endpoint": 0,
                "property": "currentState",
                "newValue": 254,
                "prevValue": 0,
                "propertyName": "currentState",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.OPENING

    # Barrier sends an opened state
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 12,
            "args": {
                "commandClassName": "Barrier Operator",
                "commandClass": 102,
                "endpoint": 0,
                "property": "currentState",
                "newValue": 255,
                "prevValue": 254,
                "propertyName": "currentState",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # Barrier sends a closing state
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 12,
            "args": {
                "commandClassName": "Barrier Operator",
                "commandClass": 102,
                "endpoint": 0,
                "property": "currentState",
                "newValue": 252,
                "prevValue": 255,
                "propertyName": "currentState",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.CLOSING

    # Barrier sends a closed state
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 12,
            "args": {
                "commandClassName": "Barrier Operator",
                "commandClass": 102,
                "endpoint": 0,
                "property": "currentState",
                "newValue": 0,
                "prevValue": 252,
                "propertyName": "currentState",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    # Barrier sends a stopped state
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": 12,
            "args": {
                "commandClassName": "Barrier Operator",
                "commandClass": 102,
                "endpoint": 0,
                "property": "currentState",
                "newValue": 253,
                "prevValue": 252,
                "propertyName": "currentState",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state.state == STATE_UNKNOWN


async def test_motor_barrier_cover_no_primary_value(
    hass: HomeAssistant,
    client: MagicMock,
    gdc_zw062_state: dict[str, Any],
    integration: MockConfigEntry,
) -> None:
    """Test the cover entity where primary value value is None."""
    node_state = replace_value_of_zwave_value(
        gdc_zw062_state,
        [
            ZwaveValueMatcher(
                property_=CURRENT_STATE_PROPERTY,
                command_class=CommandClass.BARRIER_OPERATOR,
            )
        ],
        None,
    )
    node = Node(client, node_state)
    client.driver.controller.emit("node added", {"node": node})
    await hass.async_block_till_done()

    state = hass.states.get(GDC_COVER_ENTITY)
    assert state
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.GARAGE

    assert state.state == STATE_UNKNOWN
    assert ATTR_CURRENT_POSITION not in state.attributes


async def test_fibaro_fgr222_shutter_cover_no_tilt(
    hass: HomeAssistant,
    client: MagicMock,
    fibaro_fgr222_shutter_state: dict[str, Any],
    integration: MockConfigEntry,
) -> None:
    """Test tilt function of the Fibaro Shutter devices with tilt value is None."""
    node_state = replace_value_of_zwave_value(
        fibaro_fgr222_shutter_state,
        [
            ZwaveValueMatcher(
                property_="fibaro",
                command_class=CommandClass.MANUFACTURER_PROPRIETARY,
                property_key="venetianBlindsTilt",
            ),
            ZwaveValueMatcher(
                property_=CURRENT_VALUE_PROPERTY,
                command_class=CommandClass.SWITCH_MULTILEVEL,
            ),
        ],
        None,
    )
    node = Node(client, node_state)
    client.driver.controller.emit("node added", {"node": node})
    await hass.async_block_till_done()

    state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
    assert state
    assert state.state == STATE_UNKNOWN
    assert ATTR_CURRENT_POSITION not in state.attributes
    assert ATTR_CURRENT_TILT_POSITION not in state.attributes


async def test_fibaro_fgr223_shutter_cover_no_tilt(
    hass: HomeAssistant,
    client: MagicMock,
    fibaro_fgr223_shutter_state: dict[str, Any],
    integration: MockConfigEntry,
) -> None:
    """Test absence of tilt function for Fibaro Shutter roller blind.

    Fibaro Shutter devices can have operating mode set to roller blind (1).
    """
    node_state = replace_value_of_zwave_value(
        fibaro_fgr223_shutter_state,
        [
            ZwaveValueMatcher(
                property_=151,
                command_class=CommandClass.CONFIGURATION,
                endpoint=0,
            ),
        ],
        1,
    )
    node = Node(client, node_state)
    client.driver.controller.emit("node added", {"node": node})
    await hass.async_block_till_done()

    state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
    assert state
    assert state.state == CoverState.OPEN
    assert ATTR_CURRENT_POSITION in state.attributes
    assert ATTR_CURRENT_TILT_POSITION not in state.attributes


async def test_iblinds_v3_cover(
    hass: HomeAssistant,
    client: MagicMock,
    iblinds_v3: Node,
    integration: MockConfigEntry,
) -> None:
    """Test iBlinds v3 cover which uses Window Covering CC."""
    entity_id = "cover.blind_west_bed_1_horizontal_slats_angle"
    state = hass.states.get(entity_id)
    assert state
    # This device has no state because there is no position value
    assert state.state == STATE_UNKNOWN
    assert state.attributes[ATTR_SUPPORTED_FEATURES] == (
        CoverEntityFeature.CLOSE_TILT
        | CoverEntityFeature.OPEN_TILT
        | CoverEntityFeature.SET_TILT_POSITION
        | CoverEntityFeature.STOP_TILT
    )
    assert ATTR_CURRENT_POSITION not in state.attributes
    assert ATTR_CURRENT_TILT_POSITION in state.attributes
    assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 0

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER_TILT,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 131
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 106,
        "property": "targetValue",
        "propertyKey": 23,
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER_TILT,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 131
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 106,
        "property": "targetValue",
        "propertyKey": 23,
    }
    assert args["value"] == 50

    client.async_send_command.reset_mock()

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_TILT_POSITION,
        {ATTR_ENTITY_ID: entity_id, ATTR_TILT_POSITION: 12},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 131
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 106,
        "property": "targetValue",
        "propertyKey": 23,
    }
    assert args["value"] == 12

    client.async_send_command.reset_mock()

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER_TILT,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 131
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 106,
        "property": "levelChangeUp",
        "propertyKey": 23,
    }
    assert args["value"] is False

    client.async_send_command.reset_mock()


async def test_nice_ibt4zwave_cover(
    hass: HomeAssistant,
    client: MagicMock,
    nice_ibt4zwave: Node,
    integration: MockConfigEntry,
) -> None:
    """Test Nice IBT4ZWAVE cover."""
    entity_id = "cover.portail"
    state = hass.states.get(entity_id)
    assert state
    # This device has no state because there is no position value
    assert state.state == CoverState.CLOSED
    assert state.attributes[ATTR_SUPPORTED_FEATURES] == (
        CoverEntityFeature.CLOSE
        | CoverEntityFeature.OPEN
        | CoverEntityFeature.SET_POSITION
        | CoverEntityFeature.STOP
    )
    assert ATTR_CURRENT_POSITION in state.attributes
    assert state.attributes[ATTR_CURRENT_POSITION] == 0
    assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.GATE

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 72
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 0

    client.async_send_command.reset_mock()

    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 72
    assert args["valueId"] == {
        "endpoint": 0,
        "commandClass": 38,
        "property": "targetValue",
    }
    assert args["value"] == 99

    client.async_send_command.reset_mock()


async def test_window_covering_open_close(
    hass: HomeAssistant,
    client: MagicMock,
    window_covering_outbound_bottom: Node,
    integration: MockConfigEntry,
) -> None:
    """Test Window Covering device open and close commands.

    A Window Covering device with position support
    should be able to open/close with the start/stop level change properties.
    """
    entity_id = "cover.node_2_outbound_bottom"
    state = hass.states.get(entity_id)

    # The entity has position support, but not tilt
    assert state
    assert ATTR_CURRENT_POSITION in state.attributes
    assert ATTR_CURRENT_TILT_POSITION not in state.attributes

    # Test opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 2
    assert args["valueId"] == {
        "commandClass": 106,
        "endpoint": 0,
        "property": "levelChangeUp",
        "propertyKey": 13,
    }
    assert args["value"] is True

    client.async_send_command.reset_mock()

    # Test stop after opening
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 2
    assert args["valueId"] == {
        "commandClass": 106,
        "endpoint": 0,
        "property": "levelChangeUp",
        "propertyKey": 13,
    }
    assert args["value"] is False

    client.async_send_command.reset_mock()

    # Test closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 2
    assert args["valueId"] == {
        "commandClass": 106,
        "endpoint": 0,
        "property": "levelChangeDown",
        "propertyKey": 13,
    }
    assert args["value"] is True

    client.async_send_command.reset_mock()

    # Test stop after closing
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    assert len(client.async_send_command.call_args_list) == 1
    args = client.async_send_command.call_args[0][0]
    assert args["command"] == "node.set_value"
    assert args["nodeId"] == 2
    assert args["valueId"] == {
        "commandClass": 106,
        "endpoint": 0,
        "property": "levelChangeUp",
        "propertyKey": 13,
    }
    assert args["value"] is False

    client.async_send_command.reset_mock()


async def test_multilevel_switch_cover_moving_state_working(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test opening state with Supervision WORKING on Multilevel Switch cover."""
    node = chain_actuator_zws12
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # Simulate Supervision WORKING response
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.WORKING}
    }

    # Open cover - should set OPENING state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPENING

    # Simulate intermediate position update (still moving)
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 50,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPENING

    # Simulate targetValue update (driver sets this when command is sent)
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "targetValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "targetValue",
            },
        },
    )
    node.receive_event(event)

    # Simulate reaching target position
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 50,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN


async def test_multilevel_switch_cover_moving_state_closing(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test closing state with Supervision WORKING on Multilevel Switch cover."""
    node = chain_actuator_zws12

    # First set position to open
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 99,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # Simulate Supervision WORKING response
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.WORKING}
    }

    # Close cover - should set CLOSING state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSING


async def test_multilevel_switch_cover_moving_state_success_no_moving(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test that SUCCESS does not set moving state on Multilevel Switch cover."""
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # Default mock already returns status 255 (SUCCESS)

    # Open cover - SUCCESS means device already at target, no moving state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    # State should still be CLOSED since no value update has been received
    # and SUCCESS means the command completed immediately
    assert state.state == CoverState.CLOSED


async def test_multilevel_switch_cover_moving_state_unsupervised(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test SUCCESS_UNSUPERVISED sets moving state on Multilevel Switch cover."""
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # Simulate SUCCESS_UNSUPERVISED response
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.SUCCESS_UNSUPERVISED}
    }

    # Open cover - should set OPENING state optimistically
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPENING


async def test_multilevel_switch_cover_moving_state_stop_clears(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test stop_cover clears moving state on Multilevel Switch cover."""
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # Simulate WORKING response
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.WORKING}
    }

    # Open cover to set OPENING state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPENING

    # Reset to SUCCESS for stop command
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.SUCCESS}
    }

    # Stop cover - should clear opening state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    # Cover is still at position 0 (closed), so is_closed returns True
    assert state.state == CoverState.CLOSED


async def test_multilevel_switch_cover_moving_state_set_position(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test moving direction with set_cover_position on ML Switch."""
    node = chain_actuator_zws12

    # First set position to 50 (open)
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Multilevel Switch",
                "commandClass": 38,
                "endpoint": 0,
                "property": "currentValue",
                "newValue": 50,
                "prevValue": 0,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    # Simulate WORKING response
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.WORKING}
    }

    # Set position to 20 (closing direction)
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_POSITION,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY, ATTR_POSITION: 20},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSING

    # Set position to 80 (opening direction)
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_POSITION,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY, ATTR_POSITION: 80},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPENING


async def test_window_covering_cover_moving_state(
    hass: HomeAssistant,
    client: MagicMock,
    window_covering_outbound_bottom: Node,
    integration: MockConfigEntry,
) -> None:
    """Test moving state for Window Covering CC (StartLevelChange commands)."""
    node = window_covering_outbound_bottom
    entity_id = "cover.node_2_outbound_bottom"
    state = hass.states.get(entity_id)
    assert state

    # Default mock returns SUCCESS (255). For StartLevelChange,
    # SUCCESS means the device started moving.

    # Open cover - should set OPENING state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPENING

    client.async_send_command.reset_mock()

    # Stop cover - should clear moving state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    state = hass.states.get(entity_id)
    assert state.state not in (CoverState.OPENING, CoverState.CLOSING)

    client.async_send_command.reset_mock()

    # Close cover - should set CLOSING state
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSING

    # Simulate reaching target: currentValue matches targetValue
    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Window Covering",
                "commandClass": 106,
                "endpoint": 0,
                "property": "targetValue",
                "propertyKey": 13,
                "newValue": 0,
                "prevValue": 52,
                "propertyName": "targetValue",
            },
        },
    )
    node.receive_event(event)

    event = Event(
        type="value updated",
        data={
            "source": "node",
            "event": "value updated",
            "nodeId": node.node_id,
            "args": {
                "commandClassName": "Window Covering",
                "commandClass": 106,
                "endpoint": 0,
                "property": "currentValue",
                "propertyKey": 13,
                "newValue": 0,
                "prevValue": 52,
                "propertyName": "currentValue",
            },
        },
    )
    node.receive_event(event)

    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSED


async def test_multilevel_switch_cover_moving_state_none_result(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12: Node,
    integration: MockConfigEntry,
) -> None:
    """Test None result (asleep) doesn't set moving on ML Switch."""
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # Simulate None result (node asleep/command queued).
    # When node.async_send_command returns None, async_set_value returns None.
    client.async_send_command.return_value = None

    # Open cover - should NOT set OPENING state since result is None
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED


async def test_multilevel_switch_cover_v3_no_moving_state_supervised(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12_state: dict[str, Any],
    integration: MockConfigEntry,
) -> None:
    """Test v3 ML Switch cover never sets OPENING/CLOSING with SV."""
    node_state = copy.deepcopy(chain_actuator_zws12_state)
    for value in node_state["values"]:
        if value["commandClass"] == CommandClass.SWITCH_MULTILEVEL:
            value["ccVersion"] = 3
    client.driver.controller.receive_event(
        Event(
            type="node added",
            data={
                "source": "controller",
                "event": "node added",
                "node": node_state,
                "result": {},
            },
        )
    )
    await hass.async_block_till_done()
    node = client.driver.controller.nodes[node_state["nodeId"]]

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # WORKING result (Supervision CC) must NOT optimistically set OPENING
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.WORKING}
    }
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    # Position updates still work correctly.
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Multilevel Switch",
                    "commandClass": CommandClass.SWITCH_MULTILEVEL,
                    "endpoint": 0,
                    "property": "currentValue",
                    "newValue": 99,
                    "prevValue": 0,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # WORKING result must NOT optimistically set CLOSING
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Multilevel Switch",
                    "commandClass": CommandClass.SWITCH_MULTILEVEL,
                    "endpoint": 0,
                    "property": "currentValue",
                    "newValue": 0,
                    "prevValue": 99,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    # SUCCESS result must NOT set OPENING
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.SUCCESS}
    }
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED


async def test_multilevel_switch_cover_v3_no_moving_state_unsupervised(
    hass: HomeAssistant,
    client: MagicMock,
    chain_actuator_zws12_state: dict[str, Any],
    integration: MockConfigEntry,
) -> None:
    """Test v3 ML Switch never sets OPENING/CLOSING without SV."""
    node_state = copy.deepcopy(chain_actuator_zws12_state)
    for value in node_state["values"]:
        if value["commandClass"] == CommandClass.SWITCH_MULTILEVEL:
            value["ccVersion"] = 3
    client.driver.controller.receive_event(
        Event(
            type="node added",
            data={
                "source": "controller",
                "event": "node added",
                "node": node_state,
                "result": {},
            },
        )
    )
    await hass.async_block_till_done()
    node = client.driver.controller.nodes[node_state["nodeId"]]

    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state
    assert state.state == CoverState.CLOSED

    # SUCCESS_UNSUPERVISED must NOT set OPENING
    client.async_send_command.return_value = {
        "result": {"status": SetValueStatus.SUCCESS_UNSUPERVISED}
    }
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    # Position updates still work correctly.
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Multilevel Switch",
                    "commandClass": CommandClass.SWITCH_MULTILEVEL,
                    "endpoint": 0,
                    "property": "currentValue",
                    "newValue": 99,
                    "prevValue": 0,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    # SUCCESS_UNSUPERVISED must NOT set CLOSING
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.OPEN

    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Multilevel Switch",
                    "commandClass": CommandClass.SWITCH_MULTILEVEL,
                    "endpoint": 0,
                    "property": "currentValue",
                    "newValue": 0,
                    "prevValue": 99,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED

    # SUCCESS_UNSUPERVISED set_position must NOT set OPENING
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_SET_COVER_POSITION,
        {ATTR_ENTITY_ID: WINDOW_COVER_ENTITY, ATTR_POSITION: 50},
        blocking=True,
    )
    state = hass.states.get(WINDOW_COVER_ENTITY)
    assert state.state == CoverState.CLOSED


async def test_window_covering_cover_moving_state_position_support(
    hass: HomeAssistant,
    client: MagicMock,
    window_covering_outbound_bottom: Node,
    integration: MockConfigEntry,
) -> None:
    """Test moving state is only set when not already at the target endpoint."""
    node = window_covering_outbound_bottom
    entity_id = "cover.node_2_outbound_bottom"

    # Initial currentValue is 52 (mid-position). open_cover SHOULD set OPENING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPENING

    # Clear moving state before next scenario.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    # Simulate device reaching fully open (raw Z-Wave value 99 → HA position 100%).
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "targetValue",
                    "propertyKey": 13,
                    "newValue": 99,
                    "prevValue": 52,
                    "propertyName": "targetValue",
                },
            },
        )
    )
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "currentValue",
                    "propertyKey": 13,
                    "newValue": 99,
                    "prevValue": 52,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPEN

    # Already fully open — open_cover must NOT set OPENING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state not in (CoverState.OPENING, CoverState.CLOSING)

    # Fully open but not fully closed — close_cover SHOULD set CLOSING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSING

    # Clear moving state before next scenario.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_STOP_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )

    # Simulate device reaching fully closed (raw Z-Wave value 0 → HA position 0%).
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "targetValue",
                    "propertyKey": 13,
                    "newValue": 0,
                    "prevValue": 99,
                    "propertyName": "targetValue",
                },
            },
        )
    )
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "currentValue",
                    "propertyKey": 13,
                    "newValue": 0,
                    "prevValue": 99,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSED

    # Already fully closed — close_cover must NOT set CLOSING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state not in (CoverState.OPENING, CoverState.CLOSING)

    # From fully closed, open_cover SHOULD set OPENING (not at fully open endpoint).
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPENING

    # Simulate the device moving: targetValue arrives first (early report), then
    # currentValue catches up to halfway. Moving state must stay OPENING throughout.
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "targetValue",
                    "propertyKey": 13,
                    "newValue": 99,
                    "prevValue": 0,
                    "propertyName": "targetValue",
                },
            },
        )
    )
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "currentValue",
                    "propertyKey": 13,
                    "newValue": 52,
                    "prevValue": 0,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPENING

    # Reverse halfway: close_cover while mid-travel MUST set CLOSING (not at endpoint).
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSING

    # Simulate the device moving back down: targetValue=0 arrives first (early report),
    # then currentValue reaches halfway. Moving state must stay CLOSING throughout.
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "targetValue",
                    "propertyKey": 13,
                    "newValue": 0,
                    "prevValue": 99,
                    "propertyName": "targetValue",
                },
            },
        )
    )
    node.receive_event(
        Event(
            type="value updated",
            data={
                "source": "node",
                "event": "value updated",
                "nodeId": node.node_id,
                "args": {
                    "commandClassName": "Window Covering",
                    "commandClass": 106,
                    "endpoint": 0,
                    "property": "currentValue",
                    "propertyKey": 13,
                    "newValue": 52,
                    "prevValue": 99,
                    "propertyName": "currentValue",
                },
            },
        )
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.CLOSING

    # Reverse halfway: open_cover while mid-travel MUST set OPENING (not at endpoint).
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state == CoverState.OPENING


async def test_window_covering_cover_moving_state_no_position(
    hass: HomeAssistant,
    client: MagicMock,
    window_covering_outbound_bottom_no_position: Node,
    integration: MockConfigEntry,
) -> None:
    """Test moving state not set for Window Covering without position."""
    entity_id = "cover.node_2_outbound_bottom"

    # No SET_POSITION feature — open_cover must NOT set OPENING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_OPEN_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state not in (CoverState.OPENING, CoverState.CLOSING)

    # No SET_POSITION feature — close_cover must NOT set CLOSING.
    await hass.services.async_call(
        COVER_DOMAIN,
        SERVICE_CLOSE_COVER,
        {ATTR_ENTITY_ID: entity_id},
        blocking=True,
    )
    state = hass.states.get(entity_id)
    assert state.state not in (CoverState.OPENING, CoverState.CLOSING)
