mirror of
https://github.com/python-kasa/python-kasa.git
synced 2026-07-08 06:41:59 +00:00
Some checks are pending
CI / Perform Lint Checks (3.14) (push) Waiting to run
CI / Python 3.11 on macos-latest (push) Blocked by required conditions
CI / Python 3.12 on macos-latest (push) Blocked by required conditions
CI / Python 3.13 on macos-latest (push) Blocked by required conditions
CI / Python 3.14 on macos-latest (push) Blocked by required conditions
CI / Python 3.11 on ubuntu-latest (push) Blocked by required conditions
CI / Python 3.12 on ubuntu-latest (push) Blocked by required conditions
CI / Python 3.13 on ubuntu-latest (push) Blocked by required conditions
CI / Python 3.14 on ubuntu-latest (push) Blocked by required conditions
CI / Python 3.11 on windows-latest (push) Blocked by required conditions
CI / Python 3.12 on windows-latest (push) Blocked by required conditions
CI / Python 3.13 on windows-latest (push) Blocked by required conditions
CI / Python 3.14 on windows-latest (push) Blocked by required conditions
CodeQL Checks / Analyze (python) (push) Waiting to run
Add type annotations across all 'smart' tests, enabling mypy to check test function bodies, catching type errors that were previously hidden.
147 lines
4.4 KiB
Python
147 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TypedDict
|
|
|
|
import pytest
|
|
from pytest_mock import MockerFixture
|
|
|
|
from kasa import Module
|
|
from kasa.smart import SmartDevice
|
|
from kasa.smart.modules import Alarm
|
|
from kasa.smart.modules.alarm import AlarmVolume
|
|
|
|
from ...device_fixtures import get_parent_and_child_modules, parametrize
|
|
|
|
alarm = parametrize("has alarm", component_filter="alarm", protocol_filter={"SMART"})
|
|
|
|
|
|
class PlayAlarmKwargs(TypedDict, total=False):
|
|
duration: int
|
|
volume: AlarmVolume | int
|
|
sound: str
|
|
|
|
|
|
class PlayAlarmRequestParams(TypedDict, total=False):
|
|
alarm_duration: int
|
|
alarm_volume: AlarmVolume
|
|
alarm_type: str
|
|
|
|
|
|
@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
|
|
) -> None:
|
|
"""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) -> None:
|
|
"""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: PlayAlarmKwargs,
|
|
request_params: PlayAlarmRequestParams,
|
|
mocker: MockerFixture,
|
|
) -> None:
|
|
"""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) -> None:
|
|
"""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,
|
|
) -> None:
|
|
"""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)
|