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
2 changes: 2 additions & 0 deletions kasa/modules/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
47 changes: 47 additions & 0 deletions kasa/modules/ambientlight.py
Original file line number Diff line number Diff line change
@@ -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})
62 changes: 62 additions & 0 deletions kasa/modules/motion.py
Original file line number Diff line number Diff line change
@@ -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)
5 changes: 5 additions & 0 deletions kasa/smartdimmer.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down