mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-04-26 16:46:23 +00:00
Initialize autooff features only when data is available (#933)
For power strips, the autooff data needs to be requested from the children. Until we do that, we should not create these features to avoid crashing during switch platform initialization. This also ports the module to use `_initialize_features` and add tests.
This commit is contained in:
parent
b217811096
commit
767156421b
@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from ...feature import Feature
|
from ...feature import Feature
|
||||||
from ..smartmodule import SmartModule
|
from ..smartmodule import SmartModule
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
_LOGGER = logging.getLogger(__name__)
|
||||||
from ..smartdevice import SmartDevice
|
|
||||||
|
|
||||||
|
|
||||||
class AutoOff(SmartModule):
|
class AutoOff(SmartModule):
|
||||||
@ -18,11 +17,17 @@ class AutoOff(SmartModule):
|
|||||||
REQUIRED_COMPONENT = "auto_off"
|
REQUIRED_COMPONENT = "auto_off"
|
||||||
QUERY_GETTER_NAME = "get_auto_off_config"
|
QUERY_GETTER_NAME = "get_auto_off_config"
|
||||||
|
|
||||||
def __init__(self, device: SmartDevice, module: str):
|
def _initialize_features(self):
|
||||||
super().__init__(device, module)
|
"""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(
|
self._add_feature(
|
||||||
Feature(
|
Feature(
|
||||||
device,
|
self._device,
|
||||||
id="auto_off_enabled",
|
id="auto_off_enabled",
|
||||||
name="Auto off enabled",
|
name="Auto off enabled",
|
||||||
container=self,
|
container=self,
|
||||||
@ -33,7 +38,7 @@ class AutoOff(SmartModule):
|
|||||||
)
|
)
|
||||||
self._add_feature(
|
self._add_feature(
|
||||||
Feature(
|
Feature(
|
||||||
device,
|
self._device,
|
||||||
id="auto_off_minutes",
|
id="auto_off_minutes",
|
||||||
name="Auto off minutes",
|
name="Auto off minutes",
|
||||||
container=self,
|
container=self,
|
||||||
@ -44,7 +49,7 @@ class AutoOff(SmartModule):
|
|||||||
)
|
)
|
||||||
self._add_feature(
|
self._add_feature(
|
||||||
Feature(
|
Feature(
|
||||||
device,
|
self._device,
|
||||||
id="auto_off_at",
|
id="auto_off_at",
|
||||||
name="Auto off at",
|
name="Auto off at",
|
||||||
container=self,
|
container=self,
|
||||||
|
103
kasa/tests/smart/modules/test_autooff.py
Normal file
103
kasa/tests/smart/modules/test_autooff.py
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user