mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-05-02 04:31:14 +00:00
bulb: allow set_hsv without v, add fallback ct range (#200)
* bulb: allow set_hsv without v, add fallback ct range * add ColorTempRange and HSV named tuples * add a fallback color temp range if unknown, log a warning * set_hsv: the value is now optional * Fix tests, change fallback range to 2700-5000
This commit is contained in:
parent
7565d03c8e
commit
2c83d8ee6d
kasa
@ -1,6 +1,7 @@
|
||||
"""Module for bulbs (LB*, KL*, KB*)."""
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, Dict, Tuple, cast
|
||||
from typing import Any, Dict, NamedTuple, cast
|
||||
|
||||
from kasa.smartdevice import (
|
||||
DeviceType,
|
||||
@ -9,18 +10,36 @@ from kasa.smartdevice import (
|
||||
requires_update,
|
||||
)
|
||||
|
||||
|
||||
class ColorTempRange(NamedTuple):
|
||||
"""Color temperature range."""
|
||||
|
||||
min: int
|
||||
max: int
|
||||
|
||||
|
||||
class HSV(NamedTuple):
|
||||
"""Hue-saturation-value."""
|
||||
|
||||
hue: int
|
||||
saturation: int
|
||||
value: int
|
||||
|
||||
|
||||
TPLINK_KELVIN = {
|
||||
"LB130": (2500, 9000),
|
||||
"LB120": (2700, 6500),
|
||||
"LB230": (2500, 9000),
|
||||
"KB130": (2500, 9000),
|
||||
"KL130": (2500, 9000),
|
||||
"KL125": (2500, 6500),
|
||||
r"KL120\(EU\)": (2700, 6500),
|
||||
r"KL120\(US\)": (2700, 5000),
|
||||
r"KL430": (2500, 9000),
|
||||
"LB130": ColorTempRange(2500, 9000),
|
||||
"LB120": ColorTempRange(2700, 6500),
|
||||
"LB230": ColorTempRange(2500, 9000),
|
||||
"KB130": ColorTempRange(2500, 9000),
|
||||
"KL130": ColorTempRange(2500, 9000),
|
||||
"KL125": ColorTempRange(2500, 6500),
|
||||
r"KL120\(EU\)": ColorTempRange(2700, 6500),
|
||||
r"KL120\(US\)": ColorTempRange(2700, 5000),
|
||||
r"KL430": ColorTempRange(2500, 9000),
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SmartBulb(SmartDevice):
|
||||
"""Representation of a TP-Link Smart Bulb.
|
||||
@ -69,7 +88,7 @@ class SmartBulb(SmartDevice):
|
||||
Bulbs supporting color temperature can be queried to know which range is accepted:
|
||||
|
||||
>>> bulb.valid_temperature_range
|
||||
(2500, 9000)
|
||||
ColorTempRange(min=2500, max=9000)
|
||||
>>> asyncio.run(bulb.set_color_temp(3000))
|
||||
>>> asyncio.run(bulb.update())
|
||||
>>> bulb.color_temp
|
||||
@ -80,7 +99,7 @@ class SmartBulb(SmartDevice):
|
||||
>>> asyncio.run(bulb.set_hsv(180, 100, 80))
|
||||
>>> asyncio.run(bulb.update())
|
||||
>>> bulb.hsv
|
||||
(180, 100, 80)
|
||||
HSV(hue=180, saturation=100, value=80)
|
||||
|
||||
If you don't want to use the default transitions, you can pass `transition` in milliseconds.
|
||||
This applies to all transitions (turn_on, turn_off, set_hsv, set_color_temp, set_brightness).
|
||||
@ -122,21 +141,21 @@ class SmartBulb(SmartDevice):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def valid_temperature_range(self) -> Tuple[int, int]:
|
||||
def valid_temperature_range(self) -> ColorTempRange:
|
||||
"""Return the device-specific white temperature range (in Kelvin).
|
||||
|
||||
:return: White temperature range in Kelvin (minimum, maximum)
|
||||
"""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Color temperature not supported")
|
||||
|
||||
for model, temp_range in TPLINK_KELVIN.items():
|
||||
sys_info = self.sys_info
|
||||
if re.match(model, sys_info["model"]):
|
||||
return temp_range
|
||||
|
||||
raise SmartDeviceException(
|
||||
"Unknown color temperature range, please open an issue on github"
|
||||
)
|
||||
_LOGGER.warning("Unknown color temperature range, fallback to 2700-5000")
|
||||
return ColorTempRange(2700, 5000)
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
@ -200,7 +219,7 @@ class SmartBulb(SmartDevice):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def hsv(self) -> Tuple[int, int, int]:
|
||||
def hsv(self) -> HSV:
|
||||
"""Return the current HSV state of the bulb.
|
||||
|
||||
:return: hue, saturation and value (degrees, %, %)
|
||||
@ -214,7 +233,7 @@ class SmartBulb(SmartDevice):
|
||||
saturation = light_state["saturation"]
|
||||
value = light_state["brightness"]
|
||||
|
||||
return hue, saturation, value
|
||||
return HSV(hue, saturation, value)
|
||||
|
||||
def _raise_for_invalid_brightness(self, value):
|
||||
if not isinstance(value, int) or not (0 <= value <= 100):
|
||||
@ -224,7 +243,7 @@ class SmartBulb(SmartDevice):
|
||||
|
||||
@requires_update
|
||||
async def set_hsv(
|
||||
self, hue: int, saturation: int, value: int, *, transition: int = None
|
||||
self, hue: int, saturation: int, value: int = None, *, transition: int = None
|
||||
) -> Dict:
|
||||
"""Set new HSV.
|
||||
|
||||
@ -247,15 +266,16 @@ class SmartBulb(SmartDevice):
|
||||
"(valid range: 0-100%)".format(saturation)
|
||||
)
|
||||
|
||||
self._raise_for_invalid_brightness(value)
|
||||
|
||||
light_state = {
|
||||
"hue": hue,
|
||||
"saturation": saturation,
|
||||
"brightness": value,
|
||||
"color_temp": 0,
|
||||
}
|
||||
|
||||
if value is not None:
|
||||
self._raise_for_invalid_brightness(value)
|
||||
light_state["brightness"] = value
|
||||
|
||||
return await self.set_light_state(light_state, transition=transition)
|
||||
|
||||
@property # type: ignore
|
||||
@ -284,7 +304,7 @@ class SmartBulb(SmartDevice):
|
||||
if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]:
|
||||
raise ValueError(
|
||||
"Temperature should be between {} "
|
||||
"and {}".format(*valid_temperature_range)
|
||||
"and {}, was {}".format(*valid_temperature_range, temp)
|
||||
)
|
||||
|
||||
light_state = {"color_temp": temp}
|
||||
|
@ -148,10 +148,11 @@ async def test_set_color_temp_transition(dev, mocker):
|
||||
|
||||
|
||||
@variable_temp
|
||||
async def test_unknown_temp_range(dev, monkeypatch):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
monkeypatch.setitem(dev._sys_info, "model", "unknown bulb")
|
||||
dev.valid_temperature_range()
|
||||
async def test_unknown_temp_range(dev, monkeypatch, caplog):
|
||||
monkeypatch.setitem(dev._sys_info, "model", "unknown bulb")
|
||||
|
||||
assert dev.valid_temperature_range == (2700, 5000)
|
||||
assert "Unknown color temperature range, fallback to 2700-5000" in caplog.text
|
||||
|
||||
|
||||
@variable_temp
|
||||
|
Loading…
x
Reference in New Issue
Block a user