Adjust cover reproduce state to prefer setting positions if supported (#143226)

This commit is contained in:
J. Nick Koston 2025-04-18 11:34:33 -10:00 committed by GitHub
parent 5541de2bcb
commit a7922690c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 84 additions and 32 deletions

View File

@ -73,14 +73,14 @@ async def _async_set_position(
Returns True if the position was set, False if there is no Returns True if the position was set, False if there is no
supported method for setting the position. supported method for setting the position.
""" """
if target_position == FULL_CLOSE and CoverEntityFeature.CLOSE in features: if CoverEntityFeature.SET_POSITION in features:
await service_call(SERVICE_CLOSE_COVER, service_data)
elif target_position == FULL_OPEN and CoverEntityFeature.OPEN in features:
await service_call(SERVICE_OPEN_COVER, service_data)
elif CoverEntityFeature.SET_POSITION in features:
await service_call( await service_call(
SERVICE_SET_COVER_POSITION, service_data | {ATTR_POSITION: target_position} SERVICE_SET_COVER_POSITION, service_data | {ATTR_POSITION: target_position}
) )
elif target_position == FULL_CLOSE and CoverEntityFeature.CLOSE in features:
await service_call(SERVICE_CLOSE_COVER, service_data)
elif target_position == FULL_OPEN and CoverEntityFeature.OPEN in features:
await service_call(SERVICE_OPEN_COVER, service_data)
else: else:
# Requested a position but the cover doesn't support it # Requested a position but the cover doesn't support it
return False return False
@ -98,15 +98,17 @@ async def _async_set_tilt_position(
Returns True if the tilt position was set, False if there is no Returns True if the tilt position was set, False if there is no
supported method for setting the tilt position. supported method for setting the tilt position.
""" """
if target_tilt_position == FULL_CLOSE and CoverEntityFeature.CLOSE_TILT in features: if CoverEntityFeature.SET_TILT_POSITION in features:
await service_call(SERVICE_CLOSE_COVER_TILT, service_data)
elif target_tilt_position == FULL_OPEN and CoverEntityFeature.OPEN_TILT in features:
await service_call(SERVICE_OPEN_COVER_TILT, service_data)
elif CoverEntityFeature.SET_TILT_POSITION in features:
await service_call( await service_call(
SERVICE_SET_COVER_TILT_POSITION, SERVICE_SET_COVER_TILT_POSITION,
service_data | {ATTR_TILT_POSITION: target_tilt_position}, service_data | {ATTR_TILT_POSITION: target_tilt_position},
) )
elif (
target_tilt_position == FULL_CLOSE and CoverEntityFeature.CLOSE_TILT in features
):
await service_call(SERVICE_CLOSE_COVER_TILT, service_data)
elif target_tilt_position == FULL_OPEN and CoverEntityFeature.OPEN_TILT in features:
await service_call(SERVICE_OPEN_COVER_TILT, service_data)
else: else:
# Requested a tilt position but the cover doesn't support it # Requested a tilt position but the cover doesn't support it
return False return False
@ -183,12 +185,12 @@ async def _async_reproduce_state(
current_attrs = cur_state.attributes current_attrs = cur_state.attributes
target_attrs = state.attributes target_attrs = state.attributes
current_position = current_attrs.get(ATTR_CURRENT_POSITION) current_position: int | None = current_attrs.get(ATTR_CURRENT_POSITION)
target_position = target_attrs.get(ATTR_CURRENT_POSITION) target_position: int | None = target_attrs.get(ATTR_CURRENT_POSITION)
position_matches = current_position == target_position position_matches = current_position == target_position
current_tilt_position = current_attrs.get(ATTR_CURRENT_TILT_POSITION) current_tilt_position: int | None = current_attrs.get(ATTR_CURRENT_TILT_POSITION)
target_tilt_position = target_attrs.get(ATTR_CURRENT_TILT_POSITION) target_tilt_position: int | None = target_attrs.get(ATTR_CURRENT_TILT_POSITION)
tilt_position_matches = current_tilt_position == target_tilt_position tilt_position_matches = current_tilt_position == target_tilt_position
state_matches = cur_state.state == target_state state_matches = cur_state.state == target_state
@ -214,20 +216,12 @@ async def _async_reproduce_state(
) )
service_data = {ATTR_ENTITY_ID: entity_id} service_data = {ATTR_ENTITY_ID: entity_id}
set_position = ( set_position = target_position is not None and await _async_set_position(
not position_matches
and target_position is not None
and await _async_set_position(
service_call, service_data, features, target_position service_call, service_data, features, target_position
) )
) set_tilt = target_tilt_position is not None and await _async_set_tilt_position(
set_tilt = (
not tilt_position_matches
and target_tilt_position is not None
and await _async_set_tilt_position(
service_call, service_data, features, target_tilt_position service_call, service_data, features, target_tilt_position
) )
)
if target_state in CLOSING_STATES: if target_state in CLOSING_STATES:
await _async_close_cover( await _async_close_cover(

View File

@ -178,6 +178,22 @@ async def test_reproducing_states(
| CoverEntityFeature.OPEN, | CoverEntityFeature.OPEN,
}, },
) )
hass.states.async_set(
"cover.closed_supports_all_features",
CoverState.CLOSED,
{
ATTR_CURRENT_POSITION: 0,
ATTR_CURRENT_TILT_POSITION: 0,
ATTR_SUPPORTED_FEATURES: CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION
| CoverEntityFeature.STOP
| CoverEntityFeature.OPEN_TILT
| CoverEntityFeature.CLOSE_TILT
| CoverEntityFeature.STOP_TILT
| CoverEntityFeature.SET_TILT_POSITION,
},
)
hass.states.async_set( hass.states.async_set(
"cover.tilt_only_open", "cover.tilt_only_open",
CoverState.OPEN, CoverState.OPEN,
@ -249,6 +265,14 @@ async def test_reproducing_states(
await async_reproduce_state( await async_reproduce_state(
hass, hass,
[ [
State(
"cover.closed_supports_all_features",
CoverState.CLOSED,
{
ATTR_CURRENT_POSITION: 0,
ATTR_CURRENT_TILT_POSITION: 0,
},
),
State("cover.entity_close", CoverState.CLOSED), State("cover.entity_close", CoverState.CLOSED),
State("cover.closed_only_supports_close_open", CoverState.CLOSED), State("cover.closed_only_supports_close_open", CoverState.CLOSED),
State("cover.closed_only_supports_tilt_close_open", CoverState.CLOSED), State("cover.closed_only_supports_tilt_close_open", CoverState.CLOSED),
@ -364,6 +388,11 @@ async def test_reproducing_states(
await async_reproduce_state( await async_reproduce_state(
hass, hass,
[ [
State(
"cover.closed_supports_all_features",
CoverState.CLOSED,
{ATTR_CURRENT_POSITION: 0, ATTR_CURRENT_TILT_POSITION: 50},
),
State("cover.entity_close", CoverState.OPEN), State("cover.entity_close", CoverState.OPEN),
State( State(
"cover.closed_only_supports_close_open", "cover.closed_only_supports_close_open",
@ -458,7 +487,6 @@ async def test_reproducing_states(
valid_close_calls = [ valid_close_calls = [
{"entity_id": "cover.entity_open"}, {"entity_id": "cover.entity_open"},
{"entity_id": "cover.entity_open_attr"}, {"entity_id": "cover.entity_open_attr"},
{"entity_id": "cover.entity_entirely_open"},
{"entity_id": "cover.open_only_supports_close_open"}, {"entity_id": "cover.open_only_supports_close_open"},
{"entity_id": "cover.open_missing_all_features"}, {"entity_id": "cover.open_missing_all_features"},
] ]
@ -481,11 +509,8 @@ async def test_reproducing_states(
valid_open_calls.remove(call.data) valid_open_calls.remove(call.data)
valid_close_tilt_calls = [ valid_close_tilt_calls = [
{"entity_id": "cover.entity_open_tilt"},
{"entity_id": "cover.entity_entirely_open"},
{"entity_id": "cover.tilt_only_open"}, {"entity_id": "cover.tilt_only_open"},
{"entity_id": "cover.entity_open_attr"}, {"entity_id": "cover.entity_open_attr"},
{"entity_id": "cover.tilt_only_tilt_position_100"},
{"entity_id": "cover.open_only_supports_tilt_close_open"}, {"entity_id": "cover.open_only_supports_tilt_close_open"},
] ]
assert len(close_tilt_calls) == len(valid_close_tilt_calls) assert len(close_tilt_calls) == len(valid_close_tilt_calls)
@ -495,9 +520,7 @@ async def test_reproducing_states(
valid_close_tilt_calls.remove(call.data) valid_close_tilt_calls.remove(call.data)
valid_open_tilt_calls = [ valid_open_tilt_calls = [
{"entity_id": "cover.entity_close_tilt"},
{"entity_id": "cover.tilt_only_closed"}, {"entity_id": "cover.tilt_only_closed"},
{"entity_id": "cover.tilt_only_tilt_position_0"},
{"entity_id": "cover.closed_only_supports_tilt_close_open"}, {"entity_id": "cover.closed_only_supports_tilt_close_open"},
] ]
assert len(open_tilt_calls) == len(valid_open_tilt_calls) assert len(open_tilt_calls) == len(valid_open_tilt_calls)
@ -523,6 +546,14 @@ async def test_reproducing_states(
"entity_id": "cover.open_only_supports_position", "entity_id": "cover.open_only_supports_position",
ATTR_POSITION: 0, ATTR_POSITION: 0,
}, },
{
"entity_id": "cover.closed_supports_all_features",
ATTR_POSITION: 0,
},
{
"entity_id": "cover.entity_entirely_open",
ATTR_POSITION: 0,
},
] ]
assert len(position_calls) == len(valid_position_calls) assert len(position_calls) == len(valid_position_calls)
for call in position_calls: for call in position_calls:
@ -551,7 +582,34 @@ async def test_reproducing_states(
"entity_id": "cover.tilt_partial_open_only_supports_tilt_position", "entity_id": "cover.tilt_partial_open_only_supports_tilt_position",
ATTR_TILT_POSITION: 70, ATTR_TILT_POSITION: 70,
}, },
{
"entity_id": "cover.closed_supports_all_features",
ATTR_TILT_POSITION: 50,
},
{
"entity_id": "cover.entity_close_tilt",
ATTR_TILT_POSITION: 100,
},
{
"entity_id": "cover.entity_open_tilt",
ATTR_TILT_POSITION: 0,
},
{
"entity_id": "cover.entity_entirely_open",
ATTR_TILT_POSITION: 0,
},
{
"entity_id": "cover.tilt_only_tilt_position_100",
ATTR_TILT_POSITION: 0,
},
{
"entity_id": "cover.tilt_only_tilt_position_0",
ATTR_TILT_POSITION: 100,
},
] ]
for call in position_tilt_calls:
if ATTR_TILT_POSITION not in call.data:
continue
assert len(position_tilt_calls) == len(valid_position_tilt_calls) assert len(position_tilt_calls) == len(valid_position_tilt_calls)
for call in position_tilt_calls: for call in position_tilt_calls:
assert call.domain == "cover" assert call.domain == "cover"