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
21 changes: 13 additions & 8 deletions kasa/smart/modules/autooff.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

from __future__ import annotations

import logging
from datetime import datetime, timedelta
from typing import TYPE_CHECKING

from ...feature import Feature
from ..smartmodule import SmartModule

if TYPE_CHECKING:
from ..smartdevice import SmartDevice
_LOGGER = logging.getLogger(__name__)


class AutoOff(SmartModule):
Expand All @@ -18,11 +17,17 @@ class AutoOff(SmartModule):
REQUIRED_COMPONENT = "auto_off"
QUERY_GETTER_NAME = "get_auto_off_config"

def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
def _initialize_features(self):
"""Initialize features after the initial update."""
if not isinstance(self.data, dict):
_LOGGER.warning(
"No data available for module, skipping %s: %s", self, self.data
)
return

self._add_feature(
Feature(
device,
self._device,
id="auto_off_enabled",
name="Auto off enabled",
container=self,
Expand All @@ -33,7 +38,7 @@ def __init__(self, device: SmartDevice, module: str):
)
self._add_feature(
Feature(
device,
self._device,
id="auto_off_minutes",
name="Auto off minutes",
container=self,
Expand All @@ -44,7 +49,7 @@ def __init__(self, device: SmartDevice, module: str):
)
self._add_feature(
Feature(
device,
self._device,
id="auto_off_at",
name="Auto off at",
container=self,
Expand Down
103 changes: 103 additions & 0 deletions kasa/tests/smart/modules/test_autooff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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
from kasa.tests.device_fixtures import parametrize

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."""
autooff = dev.modules.get(Module.AutoOff)
assert autooff is not None

prop = getattr(autooff, prop_name)
assert isinstance(prop, type)

feat = dev.features[feature]
assert feat.value == prop
assert isinstance(feat.value, type)


@autooff
async def test_settings(dev: SmartDevice, mocker: MockerFixture):
"""Test autooff settings."""
autooff = dev.modules.get(Module.AutoOff)
assert autooff

enabled = dev.features["auto_off_enabled"]
assert autooff.enabled == enabled.value

delay = dev.features["auto_off_minutes"]
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."""
autooff = dev.modules.get(Module.AutoOff)
assert autooff

autooff_at = dev.features["auto_off_at"]

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