Skip to content
This repository has been archived by the owner on May 9, 2024. It is now read-only.

Commit

Permalink
add support for windowShadeLevel capabaility (#72)
Browse files Browse the repository at this point in the history
* update aiohttp to 3.8.4 to fix build issue

* add support for windowShadeLevel capability and
setShadeLevel command. Fixes #67

* support shade_level attribute

* add some set_window_shade_level tests

* update black version to fix error due to click

* update README to include set_window_shade_level()
documentation

* add shade_level property tests
  • Loading branch information
jvert authored Jul 10, 2023
1 parent 2102178 commit 6e221da
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 2 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,10 @@ Devices with the `switchLevel` capability have the following function that sets
result = await device.set_level(75, 2)
assert result == True
```

Devices with the `windowShadeLevel` capability have the following function that sets the target shade level.

```pythonstub
result = await device.set_window_shade_level(50)
assert result == True
```
2 changes: 2 additions & 0 deletions pysmartthings/capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class Capability:
washer_operating_state = "washerOperatingState"
water_sensor = "waterSensor"
window_shade = "windowShade"
window_shade_level = "windowShadeLevel"


class Attribute:
Expand Down Expand Up @@ -336,6 +337,7 @@ class Attribute:
rssi = "rssi"
saturation = "saturation"
schedule = "schedule"
shade_level = "shadeLevel"
smoke = "smoke"
sound = "sound"
st = "st"
Expand Down
35 changes: 35 additions & 0 deletions pysmartthings/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Command:
set_saturation = "setSaturation"
set_thermostat_fan_mode = "setThermostatFanMode"
set_thermostat_mode = "setThermostatMode"
set_shade_level = "setShadeLevel"
unlock = "unlock"
mute = "mute"
unmute = "unmute"
Expand Down Expand Up @@ -727,6 +728,18 @@ def media_title(self) -> bool:
"""Get the trackDescription attribute."""
return self._attributes["trackDescription"].value

@property
def shade_level(self) -> int:
"""Get the shadeLevel attribute, scaled 0-100."""
return int(self._attributes[Attribute.shade_level].value or 0)

@shade_level.setter
def shade_level(self, value: int):
"""Set the level of the attribute, scaled 0-100."""
if not 0 <= value <= 100:
raise ValueError("value must be scaled between 0-100.")
self.update_attribute_value(Attribute.shade_level, value)


class DeviceStatus(DeviceStatusBase):
"""Define the device status."""
Expand Down Expand Up @@ -1390,6 +1403,28 @@ async def channel_down(
component_id, Capability.tv_channel, Command.channel_down
)

async def set_window_shade_level(
self,
level: int,
set_status: bool = False,
*,
component_id: str = "main",
) -> bool:
"""Call the set shade level device command."""
if not 0 <= level <= 100:
raise ValueError("level must be scaled between 0-100.")

result = await self.command(
component_id,
Capability.window_shade_level,
Command.set_shade_level,
[level],
)
if result and set_status:
self.status.shade_level = level
self.status.switch = level > 0
return result

@property
def status(self):
"""Get the status entity of the device."""
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
aiohttp==3.8.0
aiohttp==3.8.4
2 changes: 1 addition & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
black==21.10b0
black==22.3.0
coveralls==3.3.0
flake8==4.0.1
flake8-docstrings==1.6.0
Expand Down
10 changes: 10 additions & 0 deletions tests/json/device_command_post_set_shade_level.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"commands": [
{
"component": "main",
"capability": "windowShadeLevel",
"command": "setShadeLevel",
"arguments": [75]
}
]
}
59 changes: 59 additions & 0 deletions tests/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,44 @@ async def test_channel_down(api):
# Assert
assert result

@staticmethod
@pytest.mark.asyncio
async def test_set_window_shade_level(api):
"""Tests the set_window_shade_level method."""
# Arrange
device = DeviceEntity(api, device_id=DEVICE_ID)
# Act
result = await device.set_window_shade_level(75)
# Assert
assert result
assert device.status.level == 0
assert not device.status.switch

@staticmethod
@pytest.mark.asyncio
async def test_set_window_shade_level_invalid(api):
"""Tests the set_window_shade_level method invalid values."""
# Arrange
device = DeviceEntity(api, device_id=DEVICE_ID)
# Assert level
levels = [-1, 101]
for level in levels:
with pytest.raises(ValueError):
await device.set_window_shade_level(level)

@staticmethod
@pytest.mark.asyncio
async def test_set_window_shade_level_update(api):
"""Tests the set_window_shade_level method."""
# Arrange
device = DeviceEntity(api, device_id=DEVICE_ID)
# Act
result = await device.set_window_shade_level(75, True)
# Assert
assert result
assert device.status.shade_level == 75
assert device.status.switch


class TestDeviceStatus:
"""Tests for the DeviceStatus class."""
Expand Down Expand Up @@ -1655,3 +1693,24 @@ def test_well_known_power_consumption_attributes():
assert status.power_consumption_energy_saved is None
assert status.power_consumption_persisted_energy is None
assert status.power_consumption_power_energy is None

@staticmethod
def test_shade_level():
"""Tests the shade_level property."""
# Arrange
status = DeviceStatus(None, device_id=DEVICE_ID)
# Act
status.shade_level = 50
# Assert
assert status.shade_level == 50

@staticmethod
def test_shade_level_range():
"""Tests the shade_level property's range."""
# Arrange
status = DeviceStatus(None, device_id=DEVICE_ID)
# Act/Assert
values = [-1, 101]
for value in values:
with pytest.raises(ValueError):
status.shade_level = value

0 comments on commit 6e221da

Please sign in to comment.