Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions kasa/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class FeatureType(Enum):
Sensor = auto()
BinarySensor = auto()
Switch = auto()
Button = auto()
Action = auto()
Number = auto()


Expand All @@ -29,7 +29,7 @@ class Feature:
#: User-friendly short description
name: str
#: Name of the property that allows accessing the value
attribute_getter: str | Callable
attribute_getter: str | Callable | None = None
#: Name of the method that allows changing the value
attribute_setter: str | None = None
#: Container storing the data, this overrides 'device' for getters
Expand Down Expand Up @@ -59,6 +59,11 @@ def __post_init__(self):
@property
def value(self):
"""Return the current value."""
if self.type == FeatureType.Action:
return "<Action>"
if self.attribute_getter is None:
raise ValueError("Not an action and no attribute_getter set")

container = self.container if self.container is not None else self.device
if isinstance(self.attribute_getter, Callable):
return self.attribute_getter(container)
Expand All @@ -76,4 +81,7 @@ async def set_value(self, value):
)

container = self.container if self.container is not None else self.device
if self.type == FeatureType.Action:
return await getattr(container, self.attribute_setter)()

return await getattr(container, self.attribute_setter)(value)
22 changes: 20 additions & 2 deletions kasa/smart/modules/alarmmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ def __init__(self, device: SmartDevice, module: str):
device, "Alarm volume", container=self, attribute_getter="alarm_volume"
)
)
self._add_feature(
Feature(
device,
"Test alarm",
container=self,
attribute_setter="play",
type=FeatureType.Action,
)
)
self._add_feature(
Feature(
device,
"Stop alarm",
container=self,
attribute_setter="stop",
type=FeatureType.Action,
)
)

@property
def alarm_sound(self):
Expand Down Expand Up @@ -83,8 +101,8 @@ def source(self) -> str | None:

async def play(self):
"""Play alarm."""
return self.call("play_alarm")
return await self.call("play_alarm")

async def stop(self):
"""Stop alarm."""
return self.call("stop_alarm")
return await self.call("stop_alarm")
22 changes: 20 additions & 2 deletions kasa/tests/test_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
from kasa import Feature, FeatureType


class DummyDevice:
pass


@pytest.fixture
def dummy_feature() -> Feature:
# create_autospec for device slows tests way too much, so we use a dummy here
class DummyDevice:
pass

feat = Feature(
device=DummyDevice(), # type: ignore[arg-type]
Expand Down Expand Up @@ -77,3 +79,19 @@ async def test_feature_setter_read_only(dummy_feature):
dummy_feature.attribute_setter = None
with pytest.raises(ValueError):
await dummy_feature.set_value("value for read only feature")


async def test_feature_action(mocker):
"""Test that setting value on button calls the setter."""
feat = Feature(
device=DummyDevice(), # type: ignore[arg-type]
name="dummy_feature",
attribute_setter="call_action",
container=None,
icon="mdi:dummy",
type=FeatureType.Action,
)
mock_call_action = mocker.patch.object(feat.device, "call_action", create=True)
assert feat.value == "<Action>"
await feat.set_value(1234)
mock_call_action.assert_called()