Restore KNX cover state and mark uninitialized covers as assumed (#177216)

This commit is contained in:
Matthias Alphart
2026-07-26 01:10:22 +02:00
committed by GitHub
parent e75a891d65
commit c1eaade9d9
2 changed files with 162 additions and 8 deletions
+122 -6
View File
@@ -4,15 +4,20 @@ from typing import Any
import pytest
from homeassistant.components.cover import CoverEntityFeature, CoverState
from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
ATTR_CURRENT_TILT_POSITION,
CoverEntityFeature,
CoverState,
)
from homeassistant.components.knx.schema import CoverSchema
from homeassistant.const import CONF_NAME, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.const import CONF_NAME, STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant, State
from . import KnxEntityGenerator
from .conftest import KNXTestKit
from tests.common import async_capture_events
from tests.common import async_capture_events, mock_restore_cache
async def test_cover_basic(hass: HomeAssistant, knx: KNXTestKit) -> None:
@@ -80,6 +85,7 @@ async def test_cover_basic(hass: HomeAssistant, knx: KNXTestKit) -> None:
knx.assert_state(
"cover.test",
CoverState.CLOSING,
assumed_state=None,
)
assert len(events) == 1
@@ -169,8 +175,108 @@ async def test_cover_tilt_move_short(hass: HomeAssistant, knx: KNXTestKit) -> No
await knx.assert_write("1/0/1", 0)
async def test_cover_restore_assumed_state(
hass: HomeAssistant, knx: KNXTestKit
) -> None:
"""Test a cover without position feedback restores its state and is assumed."""
mock_restore_cache(
hass,
(
State(
"cover.test",
CoverState.OPEN,
{ATTR_CURRENT_POSITION: 40, ATTR_CURRENT_TILT_POSITION: 60},
),
),
)
await knx.setup_integration(
{
CoverSchema.PLATFORM: {
CONF_NAME: "test",
CoverSchema.CONF_MOVE_LONG_ADDRESS: "1/0/0",
CoverSchema.CONF_STOP_ADDRESS: "1/0/1",
CoverSchema.CONF_ANGLE_ADDRESS: "1/0/2",
}
}
)
# position and tilt can't be read from the bus - no telegrams are sent
await knx.assert_no_telegram()
knx.assert_state(
"cover.test",
CoverState.OPEN,
current_position=40,
current_tilt_position=60,
assumed_state=True,
)
@pytest.mark.parametrize(
("knx_data", "read_responses", "initial_state", "supported_features"),
"restored_state",
[STATE_UNKNOWN, STATE_UNAVAILABLE],
)
async def test_cover_restore_unknown_state(
hass: HomeAssistant, knx: KNXTestKit, restored_state: str
) -> None:
"""Test a cover doesn't restore a non-numeric position."""
mock_restore_cache(
hass,
(State("cover.test", restored_state, {ATTR_CURRENT_POSITION: 40}),),
)
await knx.setup_integration(
{
CoverSchema.PLATFORM: {
CONF_NAME: "test",
CoverSchema.CONF_MOVE_LONG_ADDRESS: "1/0/0",
CoverSchema.CONF_STOP_ADDRESS: "1/0/1",
}
}
)
await knx.assert_no_telegram()
knx.assert_state(
"cover.test",
STATE_UNKNOWN,
current_position=None,
assumed_state=True,
)
async def test_cover_restore_readable(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test a readable cover shows the restored state until the bus read completes."""
mock_restore_cache(
hass,
(State("cover.test", CoverState.CLOSED, {ATTR_CURRENT_POSITION: 0}),),
)
await knx.setup_integration(
{
CoverSchema.PLATFORM: {
CONF_NAME: "test",
CoverSchema.CONF_MOVE_LONG_ADDRESS: "1/0/0",
CoverSchema.CONF_POSITION_STATE_ADDRESS: "1/0/2",
CoverSchema.CONF_POSITION_ADDRESS: "1/0/3",
}
}
)
# restored value bridges the gap until the bus read completes - it is
# assumed as long as it hasn't been confirmed by the bus
knx.assert_state(
"cover.test",
CoverState.CLOSED,
current_position=0,
assumed_state=True,
)
# bus reports a different position - the confirmed value overwrites the
# restored one and the state is no longer assumed
await knx.assert_read("1/0/2", response=(0x00,))
knx.assert_state(
"cover.test",
CoverState.OPEN,
current_position=100,
assumed_state=None,
)
@pytest.mark.parametrize(
("knx_data", "read_responses", "initial_state", "supported_features", "assumed"),
[
(
{
@@ -180,6 +286,7 @@ async def test_cover_tilt_move_short(hass: HomeAssistant, knx: KNXTestKit) -> No
{},
STATE_UNKNOWN,
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE,
True,
),
(
{
@@ -192,6 +299,7 @@ async def test_cover_tilt_move_short(hass: HomeAssistant, knx: KNXTestKit) -> No
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION,
None,
),
(
{
@@ -213,6 +321,7 @@ async def test_cover_tilt_move_short(hass: HomeAssistant, knx: KNXTestKit) -> No
| CoverEntityFeature.SET_TILT_POSITION
| CoverEntityFeature.STOP
| CoverEntityFeature.STOP_TILT,
None,
),
],
)
@@ -223,6 +332,7 @@ async def test_cover_ui_create(
read_responses: dict[str, int | tuple[int]],
initial_state: str,
supported_features: int,
assumed: bool | None,
) -> None:
"""Test creating a cover."""
await knx.setup_integration()
@@ -234,7 +344,12 @@ async def test_cover_ui_create(
# created entity sends read-request to KNX bus
for ga, value in read_responses.items():
await knx.assert_read(ga, response=value, ignore_order=True)
knx.assert_state("cover.test", initial_state, supported_features=supported_features)
knx.assert_state(
"cover.test",
initial_state,
supported_features=supported_features,
assumed_state=assumed,
)
async def test_cover_ui_load(knx: KNXTestKit) -> None:
@@ -249,6 +364,7 @@ async def test_cover_ui_load(knx: KNXTestKit) -> None:
"cover.minimal",
STATE_UNKNOWN,
supported_features=CoverEntityFeature.CLOSE | CoverEntityFeature.OPEN,
assumed_state=True,
)
knx.assert_state(
"cover.position_only",