mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Implement action feature (#849)
Adds `FeatureType.Action` making it possible to expose features like "reboot", "test alarm", "pair" etc. The `attribute_getter` is no longer mandatory, but it will raise an exception if not defined for other types than actions. Trying to read returns a static string `<Action>`. This overloads the `set_value` to call the given callable on any value. This also fixes the `play` and `stop` coroutines of the alarm module to await the call.
This commit is contained in:
parent
b860c32d5f
commit
6e5cae1f47
@ -17,7 +17,7 @@ class FeatureType(Enum):
|
||||
Sensor = auto()
|
||||
BinarySensor = auto()
|
||||
Switch = auto()
|
||||
Button = auto()
|
||||
Action = auto()
|
||||
Number = auto()
|
||||
|
||||
|
||||
@ -46,7 +46,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
|
||||
@ -95,6 +95,11 @@ class Feature:
|
||||
@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)
|
||||
@ -112,6 +117,9 @@ class Feature:
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -54,6 +54,24 @@ class AlarmModule(SmartModule):
|
||||
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):
|
||||
@ -83,8 +101,8 @@ class AlarmModule(SmartModule):
|
||||
|
||||
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")
|
||||
|
@ -3,11 +3,13 @@ import pytest
|
||||
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]
|
||||
@ -79,3 +81,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()
|
||||
|
Loading…
Reference in New Issue
Block a user