mirror of
				https://github.com/python-kasa/python-kasa.git
				synced 2025-11-04 06:32:07 +00:00 
			
		
		
		
	Expose PIR enabled setting for iot dimmers (#1174)
This adds PIR enabled feature to iot dimmers, making it possible to enable and disable the motion detection.
This commit is contained in:
		@@ -2,11 +2,15 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
from enum import Enum
 | 
			
		||||
 | 
			
		||||
from ...exceptions import KasaException
 | 
			
		||||
from ...feature import Feature
 | 
			
		||||
from ..iotmodule import IotModule
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Range(Enum):
 | 
			
		||||
    """Range for motion detection."""
 | 
			
		||||
@@ -17,27 +21,51 @@ class Range(Enum):
 | 
			
		||||
    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(IotModule):
 | 
			
		||||
    """Implements the motion detection (PIR) module."""
 | 
			
		||||
 | 
			
		||||
    def _initialize_features(self):
 | 
			
		||||
        """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,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def query(self):
 | 
			
		||||
        """Request PIR configuration."""
 | 
			
		||||
        return self.query_for_command("get_config")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def config(self) -> dict:
 | 
			
		||||
        """Return current configuration."""
 | 
			
		||||
        return self.data["get_config"]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def range(self) -> Range:
 | 
			
		||||
        """Return motion detection range."""
 | 
			
		||||
        return Range(self.data["trigger_index"])
 | 
			
		||||
        return Range(self.config["trigger_index"])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def enabled(self) -> bool:
 | 
			
		||||
        """Return True if module is enabled."""
 | 
			
		||||
        return bool(self.data["enable"])
 | 
			
		||||
        return bool(self.config["enable"])
 | 
			
		||||
 | 
			
		||||
    async def set_enabled(self, state: bool):
 | 
			
		||||
        """Enable/disable PIR."""
 | 
			
		||||
@@ -63,7 +91,7 @@ class Motion(IotModule):
 | 
			
		||||
    @property
 | 
			
		||||
    def inactivity_timeout(self) -> int:
 | 
			
		||||
        """Return inactivity timeout in milliseconds."""
 | 
			
		||||
        return self.data["cold_time"]
 | 
			
		||||
        return self.config["cold_time"]
 | 
			
		||||
 | 
			
		||||
    async def set_inactivity_timeout(self, timeout: int):
 | 
			
		||||
        """Set inactivity timeout in milliseconds.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								kasa/tests/iot/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								kasa/tests/iot/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										57
									
								
								kasa/tests/iot/modules/test_motion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								kasa/tests/iot/modules/test_motion.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from kasa import Module
 | 
			
		||||
from kasa.iot import IotDimmer
 | 
			
		||||
from kasa.iot.modules.motion import Motion, Range
 | 
			
		||||
from kasa.tests.device_fixtures import dimmer_iot
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dimmer_iot
 | 
			
		||||
def test_motion_getters(dev: IotDimmer):
 | 
			
		||||
    assert Module.IotMotion in dev.modules
 | 
			
		||||
    motion: Motion = dev.modules[Module.IotMotion]
 | 
			
		||||
 | 
			
		||||
    assert motion.enabled == motion.config["enable"]
 | 
			
		||||
    assert motion.inactivity_timeout == motion.config["cold_time"]
 | 
			
		||||
    assert motion.range.value == motion.config["trigger_index"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dimmer_iot
 | 
			
		||||
async def test_motion_setters(dev: IotDimmer, mocker: MockerFixture):
 | 
			
		||||
    motion: Motion = dev.modules[Module.IotMotion]
 | 
			
		||||
    query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper")
 | 
			
		||||
 | 
			
		||||
    await motion.set_enabled(True)
 | 
			
		||||
    query_helper.assert_called_with("smartlife.iot.PIR", "set_enable", {"enable": True})
 | 
			
		||||
 | 
			
		||||
    await motion.set_inactivity_timeout(10)
 | 
			
		||||
    query_helper.assert_called_with(
 | 
			
		||||
        "smartlife.iot.PIR", "set_cold_time", {"cold_time": 10}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dimmer_iot
 | 
			
		||||
async def test_motion_range(dev: IotDimmer, mocker: MockerFixture):
 | 
			
		||||
    motion: Motion = dev.modules[Module.IotMotion]
 | 
			
		||||
    query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper")
 | 
			
		||||
 | 
			
		||||
    await motion.set_range(custom_range=123)
 | 
			
		||||
    query_helper.assert_called_with(
 | 
			
		||||
        "smartlife.iot.PIR",
 | 
			
		||||
        "set_trigger_sens",
 | 
			
		||||
        {"index": Range.Custom.value, "value": 123},
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    await motion.set_range(range=Range.Far)
 | 
			
		||||
    query_helper.assert_called_with(
 | 
			
		||||
        "smartlife.iot.PIR", "set_trigger_sens", {"index": Range.Far.value}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dimmer_iot
 | 
			
		||||
def test_motion_feature(dev: IotDimmer):
 | 
			
		||||
    assert Module.IotMotion in dev.modules
 | 
			
		||||
    motion: Motion = dev.modules[Module.IotMotion]
 | 
			
		||||
 | 
			
		||||
    pir_enabled = dev.features["pir_enabled"]
 | 
			
		||||
    assert motion.enabled == pir_enabled.value
 | 
			
		||||
		Reference in New Issue
	
	Block a user