mirror of
				https://github.com/python-kasa/python-kasa.git
				synced 2025-10-31 20:51:54 +00:00 
			
		
		
		
	Add colortemp module (#814)
Allow controlling the color temperature via features interface: ``` $ kasa --host 192.168.xx.xx feature color_temperature Color temperature (color_temperature): 0 $ kasa --host 192.168.xx.xx feature color_temperature 2000 Setting color_temperature to 2000 Raised error: Temperature should be between 2500 and 6500, was 2000 Run with --debug enabled to see stacktrace $ kasa --host 192.168.xx.xx feature color_temperature 3000 Setting color_temperature to 3000 $ kasa --host 192.168.xx.xx feature color_temperature Color temperature (color_temperature): 3000 ```
This commit is contained in:
		| @@ -41,6 +41,17 @@ class Feature: | ||||
|     minimum_value: int = 0 | ||||
|     #: Maximum value | ||||
|     maximum_value: int = 2**16  # Arbitrary max | ||||
|     #: Attribute containing the name of the range getter property. | ||||
|     #: If set, this property will be used to set *minimum_value* and *maximum_value*. | ||||
|     range_getter: Optional[str] = None | ||||
|  | ||||
|     def __post_init__(self): | ||||
|         """Handle late-binding of members.""" | ||||
|         container = self.container if self.container is not None else self.device | ||||
|         if self.range_getter is not None: | ||||
|             self.minimum_value, self.maximum_value = getattr( | ||||
|                 container, self.range_getter | ||||
|             ) | ||||
|  | ||||
|     @property | ||||
|     def value(self): | ||||
|   | ||||
| @@ -5,6 +5,7 @@ from .battery import BatterySensor | ||||
| from .brightness import Brightness | ||||
| from .childdevicemodule import ChildDeviceModule | ||||
| from .cloudmodule import CloudModule | ||||
| from .colortemp import ColorTemperatureModule | ||||
| from .devicemodule import DeviceModule | ||||
| from .energymodule import EnergyModule | ||||
| from .firmware import Firmware | ||||
| @@ -31,4 +32,5 @@ __all__ = [ | ||||
|     "Firmware", | ||||
|     "CloudModule", | ||||
|     "LightTransitionModule", | ||||
|     "ColorTemperatureModule", | ||||
| ] | ||||
|   | ||||
							
								
								
									
										55
									
								
								kasa/smart/modules/colortemp.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								kasa/smart/modules/colortemp.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| """Implementation of color temp module.""" | ||||
| from typing import TYPE_CHECKING, Dict | ||||
|  | ||||
| from ...bulb import ColorTempRange | ||||
| from ...feature import Feature | ||||
| from ..smartmodule import SmartModule | ||||
|  | ||||
| if TYPE_CHECKING: | ||||
|     from ..smartdevice import SmartDevice | ||||
|  | ||||
|  | ||||
| class ColorTemperatureModule(SmartModule): | ||||
|     """Implementation of color temp module.""" | ||||
|  | ||||
|     REQUIRED_COMPONENT = "color_temperature" | ||||
|  | ||||
|     def __init__(self, device: "SmartDevice", module: str): | ||||
|         super().__init__(device, module) | ||||
|         self._add_feature( | ||||
|             Feature( | ||||
|                 device, | ||||
|                 "Color temperature", | ||||
|                 container=self, | ||||
|                 attribute_getter="color_temp", | ||||
|                 attribute_setter="set_color_temp", | ||||
|                 range_getter="valid_temperature_range", | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|     def query(self) -> Dict: | ||||
|         """Query to execute during the update cycle.""" | ||||
|         # Color temp is contained in the main device info response. | ||||
|         return {} | ||||
|  | ||||
|     @property | ||||
|     def valid_temperature_range(self) -> ColorTempRange: | ||||
|         """Return valid color-temp range.""" | ||||
|         return ColorTempRange(*self.data.get("color_temp_range")) | ||||
|  | ||||
|     @property | ||||
|     def color_temp(self): | ||||
|         """Return current color temperature.""" | ||||
|         return self.data["color_temp"] | ||||
|  | ||||
|     async def set_color_temp(self, temp: int): | ||||
|         """Set the color temperature.""" | ||||
|         valid_temperature_range = self.valid_temperature_range | ||||
|         if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]: | ||||
|             raise ValueError( | ||||
|                 "Temperature should be between {} and {}, was {}".format( | ||||
|                     *valid_temperature_range, temp | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         return await self.call("set_device_info", {"color_temp": temp}) | ||||
							
								
								
									
										0
									
								
								kasa/tests/smart/features/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								kasa/tests/smart/features/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										31
									
								
								kasa/tests/smart/features/test_colortemp.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								kasa/tests/smart/features/test_colortemp.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import pytest | ||||
|  | ||||
| from kasa.smart import SmartDevice | ||||
| from kasa.tests.conftest import parametrize | ||||
|  | ||||
| brightness = parametrize("colortemp smart", component_filter="color_temperature") | ||||
|  | ||||
|  | ||||
| @brightness | ||||
| async def test_colortemp_component(dev: SmartDevice): | ||||
|     """Test brightness feature.""" | ||||
|     assert isinstance(dev, SmartDevice) | ||||
|     assert "color_temperature" in dev._components | ||||
|  | ||||
|     # Test getting the value | ||||
|     feature = dev.features["color_temperature"] | ||||
|     assert isinstance(feature.value, int) | ||||
|     assert isinstance(feature.minimum_value, int) | ||||
|     assert isinstance(feature.maximum_value, int) | ||||
|  | ||||
|     # Test setting the value | ||||
|     # We need to take the min here, as L9xx reports a range [9000, 9000]. | ||||
|     new_value = min(feature.minimum_value + 1, feature.maximum_value) | ||||
|     await feature.set_value(new_value) | ||||
|     assert feature.value == new_value | ||||
|  | ||||
|     with pytest.raises(ValueError): | ||||
|         await feature.set_value(feature.minimum_value - 10) | ||||
|  | ||||
|     with pytest.raises(ValueError): | ||||
|         await feature.set_value(feature.maximum_value + 10) | ||||
		Reference in New Issue
	
	Block a user
	 Teemu R
					Teemu R