mirror of
https://github.com/home-assistant/core.git
synced 2026-08-28 18:24:50 -05:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
234 lines
7.0 KiB
Python
234 lines
7.0 KiB
Python
"""Test OpenGarage cover entity."""
|
|
|
|
from datetime import timedelta
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from homeassistant.components.cover import (
|
|
ATTR_CURRENT_POSITION,
|
|
DOMAIN as COVER_DOMAIN,
|
|
SERVICE_CLOSE_COVER,
|
|
SERVICE_OPEN_COVER,
|
|
SERVICE_TOGGLE,
|
|
CoverState,
|
|
)
|
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_CLOSED, STATE_OPEN
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
|
|
async def _simulate_door_state(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
door_state: int,
|
|
) -> None:
|
|
"""Simulate the OpenGarage device reporting a new door state."""
|
|
mock_opengarage.update_state.return_value = {"door": door_state, "name": "abcdef"}
|
|
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=5))
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("door_state", "expected_state", "expected_position"),
|
|
[
|
|
(0, STATE_CLOSED, 0),
|
|
(1, STATE_OPEN, 100),
|
|
],
|
|
ids=["closed", "open"],
|
|
)
|
|
async def test_cover_position(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
init_integration: MockConfigEntry,
|
|
door_state: int,
|
|
expected_state: str,
|
|
expected_position: int,
|
|
) -> None:
|
|
"""Test that current_cover_position reflects the door state."""
|
|
mock_opengarage.push_button.return_value = 1
|
|
await _simulate_door_state(hass, mock_opengarage, door_state)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == expected_state
|
|
assert state.attributes[ATTR_CURRENT_POSITION] == expected_position
|
|
|
|
|
|
async def test_cover_position_during_transition(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
init_integration: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that current_cover_position is None during opening/closing transition."""
|
|
mock_opengarage.push_button.return_value = 1
|
|
await _simulate_door_state(hass, mock_opengarage, 0)
|
|
|
|
# Open the cover - will be in OPENING state before update
|
|
await hass.services.async_call(
|
|
COVER_DOMAIN,
|
|
SERVICE_OPEN_COVER,
|
|
{ATTR_ENTITY_ID: "cover.garage_abcdef"},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == CoverState.OPENING
|
|
assert ATTR_CURRENT_POSITION not in state.attributes
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
(
|
|
"initial_door_state",
|
|
"initial_state",
|
|
"initial_position",
|
|
"final_door_state",
|
|
"final_state",
|
|
"final_position",
|
|
),
|
|
[
|
|
(0, STATE_CLOSED, 0, 1, STATE_OPEN, 100),
|
|
(1, STATE_OPEN, 100, 0, STATE_CLOSED, 0),
|
|
],
|
|
ids=["closed_to_open", "open_to_closed"],
|
|
)
|
|
async def test_toggle_cover(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
init_integration: MockConfigEntry,
|
|
initial_door_state: int,
|
|
initial_state: str,
|
|
initial_position: int,
|
|
final_door_state: int,
|
|
final_state: str,
|
|
final_position: int,
|
|
) -> None:
|
|
"""Test toggling the cover switches it to the opposite state."""
|
|
mock_opengarage.push_button.return_value = 1
|
|
await _simulate_door_state(hass, mock_opengarage, initial_door_state)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == initial_state
|
|
assert state.attributes[ATTR_CURRENT_POSITION] == initial_position
|
|
|
|
await hass.services.async_call(
|
|
COVER_DOMAIN,
|
|
SERVICE_TOGGLE,
|
|
{ATTR_ENTITY_ID: "cover.garage_abcdef"},
|
|
blocking=True,
|
|
)
|
|
|
|
assert mock_opengarage.push_button.call_count == 1
|
|
|
|
await _simulate_door_state(hass, mock_opengarage, final_door_state)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == final_state
|
|
assert state.attributes[ATTR_CURRENT_POSITION] == final_position
|
|
|
|
|
|
async def test_toggle_does_not_reuse_stale_close_direction(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
init_integration: MockConfigEntry,
|
|
) -> None:
|
|
"""Test toggle direction is derived from position, not a stale cached direction.
|
|
|
|
This is the regression test for issue #115827. A previous close records
|
|
the "closing" direction internally; if a later toggle relies on that
|
|
cached direction instead of the current position, it would incorrectly
|
|
open an already-open cover instead of closing it.
|
|
"""
|
|
mock_opengarage.push_button.return_value = 1
|
|
await _simulate_door_state(hass, mock_opengarage, 1)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == STATE_OPEN
|
|
|
|
# Toggle while open should close, recording the close direction.
|
|
await hass.services.async_call(
|
|
COVER_DOMAIN,
|
|
SERVICE_TOGGLE,
|
|
{ATTR_ENTITY_ID: "cover.garage_abcdef"},
|
|
blocking=True,
|
|
)
|
|
assert mock_opengarage.push_button.call_count == 1
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == CoverState.CLOSING
|
|
|
|
# Door still reports open while physically closing.
|
|
await _simulate_door_state(hass, mock_opengarage, 1)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == CoverState.CLOSING
|
|
|
|
# Door finishes closing.
|
|
await _simulate_door_state(hass, mock_opengarage, 0)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == STATE_CLOSED
|
|
|
|
# Someone reopens the door outside of HA.
|
|
await _simulate_door_state(hass, mock_opengarage, 1)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == STATE_OPEN
|
|
|
|
# Toggling now must close again, not reuse the stale close direction.
|
|
await hass.services.async_call(
|
|
COVER_DOMAIN,
|
|
SERVICE_TOGGLE,
|
|
{ATTR_ENTITY_ID: "cover.garage_abcdef"},
|
|
blocking=True,
|
|
)
|
|
assert mock_opengarage.push_button.call_count == 2
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == CoverState.CLOSING
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
(
|
|
"initial_door_state",
|
|
"service",
|
|
"final_door_state",
|
|
"final_state",
|
|
"final_position",
|
|
),
|
|
[
|
|
(0, SERVICE_OPEN_COVER, 1, STATE_OPEN, 100),
|
|
(1, SERVICE_CLOSE_COVER, 0, STATE_CLOSED, 0),
|
|
],
|
|
ids=["open", "close"],
|
|
)
|
|
async def test_cover_command(
|
|
hass: HomeAssistant,
|
|
mock_opengarage: MagicMock,
|
|
init_integration: MockConfigEntry,
|
|
initial_door_state: int,
|
|
service: str,
|
|
final_door_state: int,
|
|
final_state: str,
|
|
final_position: int,
|
|
) -> None:
|
|
"""Test explicit open/close commands."""
|
|
mock_opengarage.push_button.return_value = 1
|
|
await _simulate_door_state(hass, mock_opengarage, initial_door_state)
|
|
|
|
await hass.services.async_call(
|
|
COVER_DOMAIN,
|
|
service,
|
|
{ATTR_ENTITY_ID: "cover.garage_abcdef"},
|
|
blocking=True,
|
|
)
|
|
|
|
assert mock_opengarage.push_button.call_count == 1
|
|
|
|
await _simulate_door_state(hass, mock_opengarage, final_door_state)
|
|
|
|
state = hass.states.get("cover.garage_abcdef")
|
|
assert state.state == final_state
|
|
assert state.attributes[ATTR_CURRENT_POSITION] == final_position
|