mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-09 20:24:02 +00:00
Allow passing alarm parameter overrides (#1340)
Allows specifying alarm parameters duration, volume and sound. Adds new feature: `alarm_duration`. Breaking change to `alarm_volume' on the `smart.Alarm` module is changed from `str` to `int` Co-authored-by: Steven B <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
@@ -134,11 +134,9 @@ class FakeSmartTransport(BaseTransport):
|
||||
"get_alarm_configure": (
|
||||
"alarm",
|
||||
{
|
||||
"get_alarm_configure": {
|
||||
"duration": 10,
|
||||
"type": "Doorbell Ring 2",
|
||||
"volume": "low",
|
||||
}
|
||||
"duration": 10,
|
||||
"type": "Doorbell Ring 2",
|
||||
"volume": "low",
|
||||
},
|
||||
),
|
||||
"get_support_alarm_type_list": (
|
||||
@@ -672,7 +670,7 @@ class FakeSmartTransport(BaseTransport):
|
||||
self.fixture_name, set()
|
||||
).add(method)
|
||||
return retval
|
||||
elif method in ["set_qs_info", "fw_download"]:
|
||||
elif method in ["set_qs_info", "fw_download", "play_alarm", "stop_alarm"]:
|
||||
return {"error_code": 0}
|
||||
elif method == "set_dynamic_light_effect_rule_enable":
|
||||
self._set_dynamic_light_effect(info, params)
|
||||
|
124
tests/smart/modules/test_alarm.py
Normal file
124
tests/smart/modules/test_alarm.py
Normal file
@@ -0,0 +1,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from kasa import Module
|
||||
from kasa.smart import SmartDevice
|
||||
from kasa.smart.modules import Alarm
|
||||
|
||||
from ...device_fixtures import get_parent_and_child_modules, parametrize
|
||||
|
||||
alarm = parametrize("has alarm", component_filter="alarm", protocol_filter={"SMART"})
|
||||
|
||||
|
||||
@alarm
|
||||
@pytest.mark.parametrize(
|
||||
("feature", "prop_name", "type"),
|
||||
[
|
||||
("alarm", "active", bool),
|
||||
("alarm_source", "source", str | None),
|
||||
("alarm_sound", "alarm_sound", str),
|
||||
("alarm_volume", "_alarm_volume_str", str),
|
||||
("alarm_volume_level", "alarm_volume", int),
|
||||
],
|
||||
)
|
||||
async def test_features(dev: SmartDevice, feature: str, prop_name: str, type: type):
|
||||
"""Test that features are registered and work as expected."""
|
||||
alarm = next(get_parent_and_child_modules(dev, Module.Alarm))
|
||||
assert alarm is not None
|
||||
|
||||
prop = getattr(alarm, prop_name)
|
||||
assert isinstance(prop, type)
|
||||
|
||||
feat = alarm._device.features[feature]
|
||||
assert feat.value == prop
|
||||
assert isinstance(feat.value, type)
|
||||
|
||||
|
||||
@alarm
|
||||
async def test_volume_feature(dev: SmartDevice):
|
||||
"""Test that volume features have correct choices and range."""
|
||||
alarm = next(get_parent_and_child_modules(dev, Module.Alarm))
|
||||
assert alarm is not None
|
||||
|
||||
volume_str_feat = alarm.get_feature("_alarm_volume_str")
|
||||
assert volume_str_feat
|
||||
|
||||
assert volume_str_feat.choices == ["mute", "low", "normal", "high"]
|
||||
|
||||
volume_int_feat = alarm.get_feature("alarm_volume")
|
||||
assert volume_int_feat.minimum_value == 0
|
||||
assert volume_int_feat.maximum_value == 3
|
||||
|
||||
|
||||
@alarm
|
||||
@pytest.mark.parametrize(
|
||||
("kwargs", "request_params"),
|
||||
[
|
||||
pytest.param({"volume": "low"}, {"alarm_volume": "low"}, id="volume"),
|
||||
pytest.param({"volume": 0}, {"alarm_volume": "mute"}, id="volume-integer"),
|
||||
pytest.param({"duration": 1}, {"alarm_duration": 1}, id="duration"),
|
||||
pytest.param(
|
||||
{"sound": "Doorbell Ring 1"}, {"alarm_type": "Doorbell Ring 1"}, id="sound"
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_play(dev: SmartDevice, kwargs, request_params, mocker: MockerFixture):
|
||||
"""Test that play parameters are handled correctly."""
|
||||
alarm: Alarm = next(get_parent_and_child_modules(dev, Module.Alarm))
|
||||
call_spy = mocker.spy(alarm, "call")
|
||||
await alarm.play(**kwargs)
|
||||
|
||||
call_spy.assert_called_with("play_alarm", request_params)
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid duration"):
|
||||
await alarm.play(duration=-1)
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid sound"):
|
||||
await alarm.play(sound="unknown")
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid volume"):
|
||||
await alarm.play(volume="unknown") # type: ignore[arg-type]
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid volume"):
|
||||
await alarm.play(volume=-1)
|
||||
|
||||
|
||||
@alarm
|
||||
async def test_stop(dev: SmartDevice, mocker: MockerFixture):
|
||||
"""Test that stop creates the correct call."""
|
||||
alarm: Alarm = next(get_parent_and_child_modules(dev, Module.Alarm))
|
||||
call_spy = mocker.spy(alarm, "call")
|
||||
await alarm.stop()
|
||||
|
||||
call_spy.assert_called_with("stop_alarm")
|
||||
|
||||
|
||||
@alarm
|
||||
@pytest.mark.parametrize(
|
||||
("method", "value", "target_key"),
|
||||
[
|
||||
pytest.param(
|
||||
"set_alarm_sound", "Doorbell Ring 1", "type", id="set_alarm_sound"
|
||||
),
|
||||
pytest.param("set_alarm_volume", "low", "volume", id="set_alarm_volume"),
|
||||
pytest.param("set_alarm_duration", 10, "duration", id="set_alarm_duration"),
|
||||
],
|
||||
)
|
||||
async def test_set_alarm_configure(
|
||||
dev: SmartDevice,
|
||||
mocker: MockerFixture,
|
||||
method: str,
|
||||
value: str | int,
|
||||
target_key: str,
|
||||
):
|
||||
"""Test that set_alarm_sound creates the correct call."""
|
||||
alarm: Alarm = next(get_parent_and_child_modules(dev, Module.Alarm))
|
||||
call_spy = mocker.spy(alarm, "call")
|
||||
await getattr(alarm, method)(value)
|
||||
|
||||
expected_params = {"duration": mocker.ANY, "type": mocker.ANY, "volume": mocker.ANY}
|
||||
expected_params[target_key] = value
|
||||
|
||||
call_spy.assert_called_with("set_alarm_configure", expected_params)
|
Reference in New Issue
Block a user