Fix SmartThings Cover Set Position (for window shades) (#96612)

* Update smartthings dependencies

* Update cover to support window_shade_level
This commit is contained in:
Andrew Sayre 2023-07-18 07:13:31 -05:00 committed by GitHub
parent 1ace9ab82e
commit 4ae69787a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 13 deletions

View file

@ -62,7 +62,11 @@ def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
# Must have one of the min_required # Must have one of the min_required
if any(capability in capabilities for capability in min_required): if any(capability in capabilities for capability in min_required):
# Return all capabilities supported/consumed # Return all capabilities supported/consumed
return min_required + [Capability.battery, Capability.switch_level] return min_required + [
Capability.battery,
Capability.switch_level,
Capability.window_shade_level,
]
return None return None
@ -74,12 +78,16 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity):
"""Initialize the cover class.""" """Initialize the cover class."""
super().__init__(device) super().__init__(device)
self._device_class = None self._device_class = None
self._current_cover_position = None
self._state = None self._state = None
self._state_attrs = None self._state_attrs = None
self._attr_supported_features = ( self._attr_supported_features = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
) )
if Capability.switch_level in device.capabilities: if (
Capability.switch_level in device.capabilities
or Capability.window_shade_level in device.capabilities
):
self._attr_supported_features |= CoverEntityFeature.SET_POSITION self._attr_supported_features |= CoverEntityFeature.SET_POSITION
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
@ -103,7 +111,12 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity):
if not self.supported_features & CoverEntityFeature.SET_POSITION: if not self.supported_features & CoverEntityFeature.SET_POSITION:
return return
# Do not set_status=True as device will report progress. # Do not set_status=True as device will report progress.
await self._device.set_level(kwargs[ATTR_POSITION], 0) if Capability.window_shade_level in self._device.capabilities:
await self._device.set_window_shade_level(
kwargs[ATTR_POSITION], set_status=False
)
else:
await self._device.set_level(kwargs[ATTR_POSITION], set_status=False)
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update the attrs of the cover.""" """Update the attrs of the cover."""
@ -117,6 +130,11 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity):
self._device_class = CoverDeviceClass.GARAGE self._device_class = CoverDeviceClass.GARAGE
self._state = VALUE_TO_STATE.get(self._device.status.door) self._state = VALUE_TO_STATE.get(self._device.status.door)
if Capability.window_shade_level in self._device.capabilities:
self._current_cover_position = self._device.status.shade_level
elif Capability.switch_level in self._device.capabilities:
self._current_cover_position = self._device.status.level
self._state_attrs = {} self._state_attrs = {}
battery = self._device.status.attributes[Attribute.battery].value battery = self._device.status.attributes[Attribute.battery].value
if battery is not None: if battery is not None:
@ -142,9 +160,7 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity):
@property @property
def current_cover_position(self) -> int | None: def current_cover_position(self) -> int | None:
"""Return current position of cover.""" """Return current position of cover."""
if not self.supported_features & CoverEntityFeature.SET_POSITION: return self._current_cover_position
return None
return self._device.status.level
@property @property
def device_class(self) -> CoverDeviceClass | None: def device_class(self) -> CoverDeviceClass | None:

View file

@ -30,5 +30,5 @@
"documentation": "https://www.home-assistant.io/integrations/smartthings", "documentation": "https://www.home-assistant.io/integrations/smartthings",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["httpsig", "pysmartapp", "pysmartthings"], "loggers": ["httpsig", "pysmartapp", "pysmartthings"],
"requirements": ["pysmartapp==0.3.3", "pysmartthings==0.7.6"] "requirements": ["pysmartapp==0.3.5", "pysmartthings==0.7.8"]
} }

View file

@ -2006,10 +2006,10 @@ pysma==0.7.3
pysmappee==0.2.29 pysmappee==0.2.29
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartapp==0.3.3 pysmartapp==0.3.5
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartthings==0.7.6 pysmartthings==0.7.8
# homeassistant.components.edl21 # homeassistant.components.edl21
pysml==0.0.12 pysml==0.0.12

View file

@ -1492,10 +1492,10 @@ pysma==0.7.3
pysmappee==0.2.29 pysmappee==0.2.29
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartapp==0.3.3 pysmartapp==0.3.5
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartthings==0.7.6 pysmartthings==0.7.8
# homeassistant.components.edl21 # homeassistant.components.edl21
pysml==0.0.12 pysml==0.0.12

View file

@ -113,8 +113,10 @@ async def test_close(hass: HomeAssistant, device_factory) -> None:
assert state.state == STATE_CLOSING assert state.state == STATE_CLOSING
async def test_set_cover_position(hass: HomeAssistant, device_factory) -> None: async def test_set_cover_position_switch_level(
"""Test the cover sets to the specific position.""" hass: HomeAssistant, device_factory
) -> None:
"""Test the cover sets to the specific position for legacy devices that use Capability.switch_level."""
# Arrange # Arrange
device = device_factory( device = device_factory(
"Shade", "Shade",
@ -140,6 +142,37 @@ async def test_set_cover_position(hass: HomeAssistant, device_factory) -> None:
assert device._api.post_device_command.call_count == 1 # type: ignore assert device._api.post_device_command.call_count == 1 # type: ignore
async def test_set_cover_position(hass: HomeAssistant, device_factory) -> None:
"""Test the cover sets to the specific position."""
# Arrange
device = device_factory(
"Shade",
[Capability.window_shade, Capability.battery, Capability.window_shade_level],
{
Attribute.window_shade: "opening",
Attribute.battery: 95,
Attribute.shade_level: 10,
},
)
await setup_platform(hass, COVER_DOMAIN, devices=[device])
# Act
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_SET_COVER_POSITION,
{ATTR_POSITION: 50, "entity_id": "all"},
blocking=True,
)
state = hass.states.get("cover.shade")
# Result of call does not update state
assert state.state == STATE_OPENING
assert state.attributes[ATTR_BATTERY_LEVEL] == 95
assert state.attributes[ATTR_CURRENT_POSITION] == 10
# Ensure API called
assert device._api.post_device_command.call_count == 1 # type: ignore
async def test_set_cover_position_unsupported( async def test_set_cover_position_unsupported(
hass: HomeAssistant, device_factory hass: HomeAssistant, device_factory
) -> None: ) -> None: