diff --git a/kasa/modules/__init__.py b/kasa/modules/__init__.py index dd9d1072..e5cb83d6 100644 --- a/kasa/modules/__init__.py +++ b/kasa/modules/__init__.py @@ -1,9 +1,11 @@ # flake8: noqa +from .ambientlight import AmbientLight from .antitheft import Antitheft from .cloud import Cloud from .countdown import Countdown from .emeter import Emeter from .module import Module +from .motion import Motion from .rulemodule import Rule, RuleModule from .schedule import Schedule from .time import Time diff --git a/kasa/modules/ambientlight.py b/kasa/modules/ambientlight.py new file mode 100644 index 00000000..963c73a3 --- /dev/null +++ b/kasa/modules/ambientlight.py @@ -0,0 +1,47 @@ +"""Implementation of the ambient light (LAS) module found in some dimmers.""" +from .module import Module + +# TODO create tests and use the config reply there +# [{"hw_id":0,"enable":0,"dark_index":1,"min_adc":0,"max_adc":2450, +# "level_array":[{"name":"cloudy","adc":490,"value":20}, +# {"name":"overcast","adc":294,"value":12}, +# {"name":"dawn","adc":222,"value":9}, +# {"name":"twilight","adc":222,"value":9}, +# {"name":"total darkness","adc":111,"value":4}, +# {"name":"custom","adc":2400,"value":97}]}] + + +class AmbientLight(Module): + """Implements ambient light controls for the motion sensor.""" + + def query(self): + """Request configuration.""" + return self.query_for_command("get_config") + + @property + def presets(self) -> dict: + """Return device-defined presets for brightness setting.""" + return self.data["level_array"] + + @property + def enabled(self) -> bool: + """Return True if the module is enabled.""" + return bool(self.data["enable"]) + + async def set_enabled(self, state: bool): + """Enable/disable LAS.""" + return await self.call("set_enable", {"enable": int(state)}) + + async def current_brightness(self) -> int: + """Return current brightness. + + Return value units. + """ + return await self.call("get_current_brt") + + async def set_brightness_limit(self, value: int): + """Set the limit when the motion sensor is inactive. + + See `presets` for preset values. Custom values are also likely allowed. + """ + return await self.call("set_brt_level", {"index": 0, "value": value}) diff --git a/kasa/modules/motion.py b/kasa/modules/motion.py new file mode 100644 index 00000000..d839ca98 --- /dev/null +++ b/kasa/modules/motion.py @@ -0,0 +1,62 @@ +"""Implementation of the motion detection (PIR) module found in some dimmers.""" +from enum import Enum +from typing import Optional + +from kasa.smartdevice import SmartDeviceException + +from .module import Module + + +class Range(Enum): + """Range for motion detection.""" + + Far = 0 + Mid = 1 + Near = 2 + Custom = 3 + + +# TODO: use the config reply in tests +# {"enable":0,"version":"1.0","trigger_index":2,"cold_time":60000, +# "min_adc":0,"max_adc":4095,"array":[80,50,20,0],"err_code":0}}} + + +class Motion(Module): + """Implements the motion detection (PIR) module.""" + + def query(self): + """Request PIR configuration.""" + return self.query_for_command("get_config") + + @property + def range(self) -> Range: + """Return motion detection range.""" + return Range(self.data["trigger_index"]) + + @property + def enabled(self) -> bool: + """Return True if module is enabled.""" + return bool(self.data["enable"]) + + async def set_enabled(self, state: bool): + """Enable/disable PIR.""" + return await self.call("set_enable", {"enable": int(state)}) + + async def set_range( + self, *, range: Optional[Range] = None, custom_range: Optional[int] = None + ): + """Set the range for the sensor. + + :param range: for using standard ranges + :param custom_range: range in decimeters, overrides the range parameter + """ + if custom_range is not None: + payload = {"index": Range.Custom.value, "value": custom_range} + elif range is not None: + payload = {"index": range.value} + else: + raise SmartDeviceException( + "Either range or custom_range need to be defined" + ) + + return await self.call("set_trigger_sens", payload) diff --git a/kasa/smartdimmer.py b/kasa/smartdimmer.py index 8e5cb152..5c06b8b9 100644 --- a/kasa/smartdimmer.py +++ b/kasa/smartdimmer.py @@ -1,6 +1,7 @@ """Module for dimmers (currently only HS220).""" from typing import Any, Dict +from kasa.modules import AmbientLight, Motion from kasa.smartdevice import DeviceType, SmartDeviceException, requires_update from kasa.smartplug import SmartPlug @@ -40,6 +41,10 @@ class SmartDimmer(SmartPlug): def __init__(self, host: str) -> None: super().__init__(host) self._device_type = DeviceType.Dimmer + # TODO: need to be verified if it's okay to call these on HS220 w/o these + # TODO: need to be figured out what's the best approach to detect support for these + self.add_module("motion", Motion(self, "smartlife.iot.PIR")) + self.add_module("ambient", AmbientLight(self, "smartlife.iot.LAS")) @property # type: ignore @requires_update