2022-01-29 16:53:18 +00:00
|
|
|
"""Implementation of the motion detection (PIR) module found in some dimmers."""
|
2024-04-16 18:21:20 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2024-10-31 10:41:11 +00:00
|
|
|
import logging
|
2022-01-29 16:53:18 +00:00
|
|
|
from enum import Enum
|
|
|
|
|
2024-02-21 15:52:55 +00:00
|
|
|
from ...exceptions import KasaException
|
2024-10-31 10:41:11 +00:00
|
|
|
from ...feature import Feature
|
2024-02-19 17:01:31 +00:00
|
|
|
from ..iotmodule import IotModule
|
2022-01-29 16:53:18 +00:00
|
|
|
|
2024-10-31 10:41:11 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2022-01-29 16:53:18 +00:00
|
|
|
|
|
|
|
class Range(Enum):
|
|
|
|
"""Range for motion detection."""
|
|
|
|
|
|
|
|
Far = 0
|
|
|
|
Mid = 1
|
|
|
|
Near = 2
|
|
|
|
Custom = 3
|
|
|
|
|
|
|
|
|
2024-02-04 15:20:08 +00:00
|
|
|
class Motion(IotModule):
|
2022-01-29 16:53:18 +00:00
|
|
|
"""Implements the motion detection (PIR) module."""
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
def _initialize_features(self) -> None:
|
2024-10-31 10:41:11 +00:00
|
|
|
"""Initialize features after the initial update."""
|
|
|
|
# Only add features if the device supports the module
|
|
|
|
if "get_config" not in self.data:
|
|
|
|
return
|
|
|
|
|
|
|
|
if "enable" not in self.config:
|
|
|
|
_LOGGER.warning("%r initialized, but no enable in response")
|
|
|
|
return
|
|
|
|
|
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
device=self._device,
|
|
|
|
container=self,
|
|
|
|
id="pir_enabled",
|
|
|
|
name="PIR enabled",
|
|
|
|
icon="mdi:motion-sensor",
|
|
|
|
attribute_getter="enabled",
|
|
|
|
attribute_setter="set_enabled",
|
|
|
|
type=Feature.Type.Switch,
|
|
|
|
category=Feature.Category.Config,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
def query(self) -> dict:
|
2022-01-29 16:53:18 +00:00
|
|
|
"""Request PIR configuration."""
|
|
|
|
return self.query_for_command("get_config")
|
|
|
|
|
2024-10-31 10:41:11 +00:00
|
|
|
@property
|
|
|
|
def config(self) -> dict:
|
|
|
|
"""Return current configuration."""
|
|
|
|
return self.data["get_config"]
|
|
|
|
|
2022-01-29 16:53:18 +00:00
|
|
|
@property
|
|
|
|
def range(self) -> Range:
|
|
|
|
"""Return motion detection range."""
|
2024-10-31 10:41:11 +00:00
|
|
|
return Range(self.config["trigger_index"])
|
2022-01-29 16:53:18 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def enabled(self) -> bool:
|
|
|
|
"""Return True if module is enabled."""
|
2024-10-31 10:41:11 +00:00
|
|
|
return bool(self.config["enable"])
|
2022-01-29 16:53:18 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def set_enabled(self, state: bool) -> dict:
|
2022-01-29 16:53:18 +00:00
|
|
|
"""Enable/disable PIR."""
|
|
|
|
return await self.call("set_enable", {"enable": int(state)})
|
|
|
|
|
|
|
|
async def set_range(
|
2024-04-17 13:39:24 +00:00
|
|
|
self, *, range: Range | None = None, custom_range: int | None = None
|
2024-11-10 18:55:13 +00:00
|
|
|
) -> dict:
|
2022-01-29 16:53:18 +00:00
|
|
|
"""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:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException("Either range or custom_range need to be defined")
|
2022-01-29 16:53:18 +00:00
|
|
|
|
|
|
|
return await self.call("set_trigger_sens", payload)
|
2023-05-17 18:08:05 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def inactivity_timeout(self) -> int:
|
|
|
|
"""Return inactivity timeout in milliseconds."""
|
2024-10-31 10:41:11 +00:00
|
|
|
return self.config["cold_time"]
|
2023-05-17 18:08:05 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def set_inactivity_timeout(self, timeout: int) -> dict:
|
2023-05-17 18:08:05 +00:00
|
|
|
"""Set inactivity timeout in milliseconds.
|
|
|
|
|
|
|
|
Note, that you need to delete the default "Smart Control" rule in the app
|
|
|
|
to avoid reverting this back to 60 seconds after a period of time.
|
|
|
|
"""
|
|
|
|
return await self.call("set_cold_time", {"cold_time": timeout})
|