Fix homematicip_cloud shutter group resetting slats on close (#172278)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
mastameista
2026-08-28 08:42:05 +02:00
committed by GitHub
co-authored by Martin Hjelmare
parent 0f426ea964
commit bef12b849b
2 changed files with 42 additions and 6 deletions
@@ -399,6 +399,12 @@ class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity):
position = kwargs[ATTR_POSITION]
# HmIP cover is closed:1 -> open:0
level = 1 - position / 100.0
if level == HMIP_COVER_CLOSED:
# Route fully-closed position through the same slats-safe call
# as async_close_cover, otherwise slats get reset to 0 on FBL
# group members. See issue #114266.
await self.async_close_cover()
return
await self._device.set_shutter_level_async(level)
@override
@@ -411,13 +417,26 @@ class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity):
@override
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
"""Open the cover.
The slats-safe call used in async_close_cover is intentionally not
mirrored here: the regression reported in issue #114266 only
affects close, and for an open cover slats at 0 (horizontal) is
the natural rest position.
"""
await self._device.set_shutter_level_async(HMIP_COVER_OPEN)
@override
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
await self._device.set_shutter_level_async(HMIP_COVER_CLOSED)
"""Close the cover.
Use setSlatsLevel instead of setShutterLevel so HMIP Cloud does
not reset slats to 0 (horizontal) on blind-capable group members
(e.g. HmIP-FBL with firmware >= 1.10.16). See issue #114266.
"""
await self._device.set_slats_level_async(
slatsLevel=HMIP_SLATS_CLOSED, shutterLevel=HMIP_COVER_CLOSED
)
@override
async def async_stop_cover(self, **kwargs: Any) -> None:
@@ -507,8 +507,25 @@ async def test_hmip_cover_shutter_group(
"cover", "close_cover", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 5
assert hmip_device.mock_calls[-1][0] == "set_shutter_level_async"
assert hmip_device.mock_calls[-1][1] == (1,)
assert hmip_device.mock_calls[-1][0] == "set_slats_level_async"
assert hmip_device.mock_calls[-1][2] == {"slatsLevel": 1, "shutterLevel": 1}
await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 1)
ha_state = hass.states.get(entity_id)
assert ha_state.state == CoverState.CLOSED
assert ha_state.attributes[ATTR_CURRENT_POSITION] == 0
# set_cover_position with position=0 must route through the same
# slats-safe call as close_cover, otherwise the FBL slats regression
# (#114266) would still hit users moving the slider to fully closed.
await hass.services.async_call(
"cover",
"set_cover_position",
{"entity_id": entity_id, "position": "0"},
blocking=True,
)
assert len(hmip_device.mock_calls) == service_call_counter + 7
assert hmip_device.mock_calls[-1][0] == "set_slats_level_async"
assert hmip_device.mock_calls[-1][2] == {"slatsLevel": 1, "shutterLevel": 1}
await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 1)
ha_state = hass.states.get(entity_id)
assert ha_state.state == CoverState.CLOSED
@@ -517,7 +534,7 @@ async def test_hmip_cover_shutter_group(
await hass.services.async_call(
"cover", "stop_cover", {"entity_id": entity_id}, blocking=True
)
assert len(hmip_device.mock_calls) == service_call_counter + 7
assert len(hmip_device.mock_calls) == service_call_counter + 9
assert hmip_device.mock_calls[-1][0] == "set_shutter_stop_async"
assert hmip_device.mock_calls[-1][1] == ()