2024-05-24 17:39:10 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import sys
|
|
|
|
from datetime import datetime
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
from pytest_mock import MockerFixture
|
|
|
|
|
|
|
|
from kasa import Module
|
|
|
|
from kasa.smart import SmartDevice
|
2024-06-10 14:47:00 +00:00
|
|
|
from kasa.tests.device_fixtures import get_parent_and_child_modules, parametrize
|
2024-05-24 17:39:10 +00:00
|
|
|
|
|
|
|
autooff = parametrize(
|
|
|
|
"has autooff", component_filter="auto_off", protocol_filter={"SMART"}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@autooff
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"feature, prop_name, type",
|
|
|
|
[
|
|
|
|
("auto_off_enabled", "enabled", bool),
|
|
|
|
("auto_off_minutes", "delay", int),
|
|
|
|
("auto_off_at", "auto_off_at", Optional[datetime]),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
sys.version_info < (3, 10),
|
|
|
|
reason="Subscripted generics cannot be used with class and instance checks",
|
|
|
|
)
|
|
|
|
async def test_autooff_features(
|
|
|
|
dev: SmartDevice, feature: str, prop_name: str, type: type
|
|
|
|
):
|
|
|
|
"""Test that features are registered and work as expected."""
|
2024-06-10 14:47:00 +00:00
|
|
|
autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
|
2024-05-24 17:39:10 +00:00
|
|
|
assert autooff is not None
|
|
|
|
|
|
|
|
prop = getattr(autooff, prop_name)
|
|
|
|
assert isinstance(prop, type)
|
|
|
|
|
2024-06-10 14:47:00 +00:00
|
|
|
feat = autooff._device.features[feature]
|
2024-05-24 17:39:10 +00:00
|
|
|
assert feat.value == prop
|
|
|
|
assert isinstance(feat.value, type)
|
|
|
|
|
|
|
|
|
|
|
|
@autooff
|
|
|
|
async def test_settings(dev: SmartDevice, mocker: MockerFixture):
|
|
|
|
"""Test autooff settings."""
|
2024-06-10 14:47:00 +00:00
|
|
|
autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
|
2024-05-24 17:39:10 +00:00
|
|
|
assert autooff
|
|
|
|
|
2024-06-10 14:47:00 +00:00
|
|
|
enabled = autooff._device.features["auto_off_enabled"]
|
2024-05-24 17:39:10 +00:00
|
|
|
assert autooff.enabled == enabled.value
|
|
|
|
|
2024-06-10 14:47:00 +00:00
|
|
|
delay = autooff._device.features["auto_off_minutes"]
|
2024-05-24 17:39:10 +00:00
|
|
|
assert autooff.delay == delay.value
|
|
|
|
|
|
|
|
call = mocker.spy(autooff, "call")
|
|
|
|
new_state = True
|
|
|
|
|
|
|
|
await autooff.set_enabled(new_state)
|
|
|
|
call.assert_called_with(
|
|
|
|
"set_auto_off_config", {"enable": new_state, "delay_min": delay.value}
|
|
|
|
)
|
|
|
|
call.reset_mock()
|
|
|
|
await dev.update()
|
|
|
|
|
|
|
|
new_delay = 123
|
|
|
|
|
|
|
|
await autooff.set_delay(new_delay)
|
|
|
|
|
|
|
|
call.assert_called_with(
|
|
|
|
"set_auto_off_config", {"enable": new_state, "delay_min": new_delay}
|
|
|
|
)
|
|
|
|
|
|
|
|
await dev.update()
|
|
|
|
|
|
|
|
assert autooff.enabled == new_state
|
|
|
|
assert autooff.delay == new_delay
|
|
|
|
|
|
|
|
|
|
|
|
@autooff
|
|
|
|
@pytest.mark.parametrize("is_timer_active", [True, False])
|
|
|
|
async def test_auto_off_at(
|
|
|
|
dev: SmartDevice, mocker: MockerFixture, is_timer_active: bool
|
|
|
|
):
|
|
|
|
"""Test auto-off at sensor."""
|
2024-06-10 14:47:00 +00:00
|
|
|
autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
|
2024-05-24 17:39:10 +00:00
|
|
|
assert autooff
|
|
|
|
|
2024-06-10 14:47:00 +00:00
|
|
|
autooff_at = autooff._device.features["auto_off_at"]
|
2024-05-24 17:39:10 +00:00
|
|
|
|
|
|
|
mocker.patch.object(
|
|
|
|
type(autooff),
|
|
|
|
"is_timer_active",
|
|
|
|
new_callable=mocker.PropertyMock,
|
|
|
|
return_value=is_timer_active,
|
|
|
|
)
|
|
|
|
if is_timer_active:
|
|
|
|
assert isinstance(autooff_at.value, datetime)
|
|
|
|
else:
|
|
|
|
assert autooff_at.value is None
|