mirror of
				https://github.com/python-kasa/python-kasa.git
				synced 2025-11-04 06:32:07 +00:00 
			
		
		
		
	Add temperature control module for smart (#848)
This commit is contained in:
		@@ -19,6 +19,7 @@ class DeviceType(Enum):
 | 
			
		||||
    Sensor = "sensor"
 | 
			
		||||
    Hub = "hub"
 | 
			
		||||
    Fan = "fan"
 | 
			
		||||
    Thermostat = "thermostat"
 | 
			
		||||
    Unknown = "unknown"
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ from .ledmodule import LedModule
 | 
			
		||||
from .lighttransitionmodule import LightTransitionModule
 | 
			
		||||
from .reportmodule import ReportModule
 | 
			
		||||
from .temperature import TemperatureSensor
 | 
			
		||||
from .temperaturecontrol import TemperatureControl
 | 
			
		||||
from .timemodule import TimeModule
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
@@ -28,6 +29,7 @@ __all__ = [
 | 
			
		||||
    "BatterySensor",
 | 
			
		||||
    "HumiditySensor",
 | 
			
		||||
    "TemperatureSensor",
 | 
			
		||||
    "TemperatureControl",
 | 
			
		||||
    "ReportModule",
 | 
			
		||||
    "AutoOffModule",
 | 
			
		||||
    "LedModule",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								kasa/smart/modules/temperaturecontrol.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								kasa/smart/modules/temperaturecontrol.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
"""Implementation of temperature control module."""
 | 
			
		||||
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
from typing import TYPE_CHECKING
 | 
			
		||||
 | 
			
		||||
from ...feature import Feature
 | 
			
		||||
from ..smartmodule import SmartModule
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from ..smartdevice import SmartDevice
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TemperatureControl(SmartModule):
 | 
			
		||||
    """Implementation of temperature module."""
 | 
			
		||||
 | 
			
		||||
    REQUIRED_COMPONENT = "temperature_control"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, device: SmartDevice, module: str):
 | 
			
		||||
        super().__init__(device, module)
 | 
			
		||||
        self._add_feature(
 | 
			
		||||
            Feature(
 | 
			
		||||
                device,
 | 
			
		||||
                "Target temperature",
 | 
			
		||||
                container=self,
 | 
			
		||||
                attribute_getter="target_temperature",
 | 
			
		||||
                attribute_setter="set_target_temperature",
 | 
			
		||||
                icon="mdi:thermometer",
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        # TODO: this might belong into its own module, temperature_correction?
 | 
			
		||||
        self._add_feature(
 | 
			
		||||
            Feature(
 | 
			
		||||
                device,
 | 
			
		||||
                "Temperature offset",
 | 
			
		||||
                container=self,
 | 
			
		||||
                attribute_getter="temperature_offset",
 | 
			
		||||
                attribute_setter="set_temperature_offset",
 | 
			
		||||
                minimum_value=-10,
 | 
			
		||||
                maximum_value=10,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def query(self) -> dict:
 | 
			
		||||
        """Query to execute during the update cycle."""
 | 
			
		||||
        # Target temperature is contained in the main device info response.
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def minimum_target_temperature(self) -> int:
 | 
			
		||||
        """Minimum available target temperature."""
 | 
			
		||||
        return self._device.sys_info["min_control_temp"]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def maximum_target_temperature(self) -> int:
 | 
			
		||||
        """Minimum available target temperature."""
 | 
			
		||||
        return self._device.sys_info["max_control_temp"]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def target_temperature(self) -> int:
 | 
			
		||||
        """Return target temperature."""
 | 
			
		||||
        return self._device.sys_info["target_temperature"]
 | 
			
		||||
 | 
			
		||||
    async def set_target_temperature(self, target: int):
 | 
			
		||||
        """Set target temperature."""
 | 
			
		||||
        if (
 | 
			
		||||
            target < self.minimum_target_temperature
 | 
			
		||||
            or target > self.maximum_target_temperature
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                f"Invalid target temperature {target}, must be in range "
 | 
			
		||||
                f"[{self.minimum_target_temperature},{self.maximum_target_temperature}]"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return await self.call("set_device_info", {"target_temp": target})
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def temperature_offset(self) -> int:
 | 
			
		||||
        """Return temperature offset."""
 | 
			
		||||
        return self._device.sys_info["temp_offset"]
 | 
			
		||||
 | 
			
		||||
    async def set_temperature_offset(self, offset: int):
 | 
			
		||||
        """Set temperature offset."""
 | 
			
		||||
        if offset < -10 or offset > 10:
 | 
			
		||||
            raise ValueError("Temperature offset must be [-10, 10]")
 | 
			
		||||
 | 
			
		||||
        return await self.call("set_device_info", {"temp_offset": offset})
 | 
			
		||||
@@ -52,6 +52,7 @@ class SmartChildDevice(SmartDevice):
 | 
			
		||||
            "subg.trigger.temp-hmdt-sensor": DeviceType.Sensor,
 | 
			
		||||
            "kasa.switch.outlet.sub-fan": DeviceType.Fan,
 | 
			
		||||
            "kasa.switch.outlet.sub-dimmer": DeviceType.Dimmer,
 | 
			
		||||
            "subg.trv": DeviceType.Thermostat,
 | 
			
		||||
        }
 | 
			
		||||
        dev_type = child_device_map.get(self.sys_info["category"])
 | 
			
		||||
        if dev_type is None:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								kasa/tests/smart/modules/test_temperaturecontrol.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								kasa/tests/smart/modules/test_temperaturecontrol.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from kasa.smart.modules import TemperatureSensor
 | 
			
		||||
from kasa.tests.device_fixtures import parametrize
 | 
			
		||||
 | 
			
		||||
temperature = parametrize(
 | 
			
		||||
    "has temperature control",
 | 
			
		||||
    component_filter="temperature_control",
 | 
			
		||||
    protocol_filter={"SMART.CHILD"},
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@temperature
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "feature, type",
 | 
			
		||||
    [
 | 
			
		||||
        ("target_temperature", int),
 | 
			
		||||
        ("temperature_offset", int),
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
async def test_temperature_control_features(dev, feature, type):
 | 
			
		||||
    """Test that features are registered and work as expected."""
 | 
			
		||||
    temp_module: TemperatureSensor = dev.modules["TemperatureControl"]
 | 
			
		||||
 | 
			
		||||
    prop = getattr(temp_module, feature)
 | 
			
		||||
    assert isinstance(prop, type)
 | 
			
		||||
 | 
			
		||||
    feat = temp_module._module_features[feature]
 | 
			
		||||
    assert feat.value == prop
 | 
			
		||||
    assert isinstance(feat.value, type)
 | 
			
		||||
 | 
			
		||||
    await feat.set_value(10)
 | 
			
		||||
    await dev.update()
 | 
			
		||||
    assert feat.value == 10
 | 
			
		||||
		Reference in New Issue
	
	Block a user