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
4 changes: 2 additions & 2 deletions kasa/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ def range(self) -> tuple[int, int] | None:
"""Range of values if applicable."""
return self._get_property_value(self.range_getter)

@cached_property
@property
def maximum_value(self) -> int:
"""Maximum value."""
if range := self.range:
return range[1]
return self.DEFAULT_MAX

@cached_property
@property
def minimum_value(self) -> int:
"""Minimum value."""
if range := self.range:
Expand Down
2 changes: 1 addition & 1 deletion kasa/iot/iotbulb.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ async def _set_color_temp(
if not self._is_variable_color_temp:
raise KasaException("Bulb does not support colortemp.")

valid_temperature_range = self.valid_temperature_range
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(
Expand Down
19 changes: 8 additions & 11 deletions kasa/smart/modules/colortemperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from ...feature import Feature
from ...interfaces.light import ColorTempRange
from ..smartmodule import SmartModule

if TYPE_CHECKING:
from ..smartdevice import SmartDevice


_LOGGER = logging.getLogger(__name__)

DEFAULT_TEMP_RANGE = [2500, 6500]
Expand All @@ -23,11 +18,11 @@ class ColorTemperature(SmartModule):

REQUIRED_COMPONENT = "color_temperature"

def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
def _initialize_features(self):
"""Initialize features."""
self._add_feature(
Feature(
device,
self._device,
"color_temperature",
"Color temperature",
container=self,
Expand Down Expand Up @@ -61,7 +56,7 @@ def color_temp(self):
"""Return current color temperature."""
return self.data["color_temp"]

async def set_color_temp(self, temp: int):
async def set_color_temp(self, temp: int, *, brightness=None):
"""Set the color temperature."""
valid_temperature_range = self.valid_temperature_range
if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]:
Expand All @@ -70,8 +65,10 @@ async def set_color_temp(self, temp: int):
*valid_temperature_range, temp
)
)

return await self.call("set_device_info", {"color_temp": temp})
params = {"color_temp": temp}
if brightness:
params["brightness"] = brightness
return await self.call("set_device_info", params)

async def _check_supported(self) -> bool:
"""Check the color_temp_range has more than one value."""
Expand Down
4 changes: 3 additions & 1 deletion kasa/smart/modules/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ async def set_color_temp(
"""
if not self.is_variable_color_temp:
raise KasaException("Bulb does not support colortemp.")
return await self._device.modules[Module.ColorTemperature].set_color_temp(temp)
return await self._device.modules[Module.ColorTemperature].set_color_temp(
temp, brightness=brightness
)

async def set_brightness(
self, brightness: int, *, transition: int | None = None
Expand Down
48 changes: 48 additions & 0 deletions kasa/tests/test_common_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
parametrize,
parametrize_combine,
plug_iot,
variable_temp_iot,
)

led_smart = parametrize(
Expand All @@ -36,6 +37,14 @@
)
dimmable = parametrize_combine([dimmable_smart, dimmer_iot, dimmable_iot])

variable_temp_smart = parametrize(
"variable temp smart",
component_filter="color_temperature",
protocol_filter={"SMART"},
)

variable_temp = parametrize_combine([variable_temp_iot, variable_temp_smart])

light_preset_smart = parametrize(
"has light preset smart", component_filter="preset", protocol_filter={"SMART"}
)
Expand Down Expand Up @@ -147,6 +156,45 @@ async def test_light_brightness(dev: Device):
await light.set_brightness(feature.maximum_value + 10)


@variable_temp
async def test_light_color_temp(dev: Device):
"""Test color temp setter and getter."""
assert isinstance(dev, Device)

light = next(get_parent_and_child_modules(dev, Module.Light))
assert light
if not light.is_variable_color_temp:
pytest.skip(
"Some smart light strips have color_temperature"
" component but min and max are the same"
)

# Test getting the value
feature = light._device.features["color_temperature"]
assert isinstance(feature.minimum_value, int)
assert isinstance(feature.maximum_value, int)

await light.set_color_temp(feature.minimum_value + 10)
await dev.update()
assert light.color_temp == feature.minimum_value + 10

# Test setting brightness with color temp
await light.set_brightness(50)
await dev.update()
assert light.brightness == 50

await light.set_color_temp(feature.minimum_value + 20, brightness=60)
await dev.update()
assert light.color_temp == feature.minimum_value + 20
assert light.brightness == 60

with pytest.raises(ValueError):
await light.set_color_temp(feature.minimum_value - 10)

with pytest.raises(ValueError):
await light.set_color_temp(feature.maximum_value + 10)


@light
async def test_light_set_state(dev: Device):
"""Test brightness setter and getter."""
Expand Down