diff --git a/kasa/feature.py b/kasa/feature.py index c0c14b06..420fd848 100644 --- a/kasa/feature.py +++ b/kasa/feature.py @@ -47,4 +47,5 @@ class Feature: """Set the value.""" if self.attribute_setter is None: raise ValueError("Tried to set read-only feature.") - return await getattr(self.device, self.attribute_setter)(value) + container = self.container if self.container is not None else self.device + return await getattr(container, self.attribute_setter)(value) diff --git a/kasa/smart/modules/__init__.py b/kasa/smart/modules/__init__.py index 5274e7b3..6031ef2a 100644 --- a/kasa/smart/modules/__init__.py +++ b/kasa/smart/modules/__init__.py @@ -1,4 +1,5 @@ """Modules for SMART devices.""" +from .autooffmodule import AutoOffModule from .childdevicemodule import ChildDeviceModule from .cloudmodule import CloudModule from .devicemodule import DeviceModule @@ -12,6 +13,7 @@ __all__ = [ "EnergyModule", "DeviceModule", "ChildDeviceModule", + "AutoOffModule", "LedModule", "CloudModule", "LightTransitionModule", diff --git a/kasa/smart/modules/autooffmodule.py b/kasa/smart/modules/autooffmodule.py new file mode 100644 index 00000000..b1993deb --- /dev/null +++ b/kasa/smart/modules/autooffmodule.py @@ -0,0 +1,84 @@ +"""Implementation of auto off module.""" +from datetime import datetime, timedelta +from typing import TYPE_CHECKING, Dict, Optional + +from ...feature import Feature +from ..smartmodule import SmartModule + +if TYPE_CHECKING: + from ..smartdevice import SmartDevice + + +class AutoOffModule(SmartModule): + """Implementation of auto off module.""" + + REQUIRED_COMPONENT = "auto_off" + QUERY_GETTER_NAME = "get_auto_off_config" + + def __init__(self, device: "SmartDevice", module: str): + super().__init__(device, module) + self._add_feature( + Feature( + device, + "Auto off enabled", + container=self, + attribute_getter="enabled", + attribute_setter="set_enabled", + ) + ) + self._add_feature( + Feature( + device, + "Auto off minutes", + container=self, + attribute_getter="delay", + attribute_setter="set_delay", + ) + ) + self._add_feature( + Feature( + device, "Auto off at", container=self, attribute_getter="auto_off_at" + ) + ) + + def query(self) -> Dict: + """Query to execute during the update cycle.""" + return {self.QUERY_GETTER_NAME: {"start_index": 0}} + + @property + def enabled(self) -> bool: + """Return True if enabled.""" + return self.data["enable"] + + def set_enabled(self, enable: bool): + """Enable/disable auto off.""" + return self.call( + "set_auto_off_config", + {"enable": enable, "delay_min": self.data["delay_min"]}, + ) + + @property + def delay(self) -> int: + """Return time until auto off.""" + return self.data["delay_min"] + + def set_delay(self, delay: int): + """Set time until auto off.""" + return self.call( + "set_auto_off_config", {"delay_min": delay, "enable": self.data["enable"]} + ) + + @property + def is_timer_active(self) -> bool: + """Return True is auto-off timer is active.""" + return self._device.sys_info["auto_off_status"] == "on" + + @property + def auto_off_at(self) -> Optional[datetime]: + """Return when the device will be turned off automatically.""" + if not self.is_timer_active: + return None + + sysinfo = self._device.sys_info + + return self._device.time + timedelta(seconds=sysinfo["auto_off_remain_time"]) diff --git a/kasa/smart/smartdevice.py b/kasa/smart/smartdevice.py index d9e859d4..62657d81 100644 --- a/kasa/smart/smartdevice.py +++ b/kasa/smart/smartdevice.py @@ -13,6 +13,7 @@ from ..exceptions import AuthenticationException, SmartDeviceException, SmartErr from ..feature import Feature, FeatureType from ..smartprotocol import SmartProtocol from .modules import ( # noqa: F401 + AutoOffModule, ChildDeviceModule, CloudModule, DeviceModule, diff --git a/kasa/tests/fakeprotocol_smart.py b/kasa/tests/fakeprotocol_smart.py index 0b04282e..4c9b034b 100644 --- a/kasa/tests/fakeprotocol_smart.py +++ b/kasa/tests/fakeprotocol_smart.py @@ -46,6 +46,7 @@ class FakeSmartTransport(BaseTransport): FIXTURE_MISSING_MAP = { "get_wireless_scan_info": ("wireless", {"ap_list": [], "wep_supported": False}), + "get_auto_off_config": ("auto_off", {'delay_min': 10, 'enable': False}), "get_led_info": ( "led", {