| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,15 +1,17 @@ | |||
| 1 | + """Module for tapo-branded smart bulbs (L5**).""" | ||
| 1 | 2 | from typing import Dict, List, Optional | |
| 2 | 3 | ||
| 4 | + from ..exceptions import SmartDeviceException | ||
| 3 | 5 | from ..smartbulb import HSV, ColorTempRange, SmartBulb, SmartBulbPreset | |
| 4 | 6 | from .tapodevice import TapoDevice | |
| 5 | 7 | ||
| 6 | 8 | AVAILABLE_EFFECTS = { | |
| 7 | - 'L1': "Party", | ||
| 9 | + "L1": "Party", | ||
| 8 | 10 | "L2": "Relax", | |
| 9 | 11 | } | |
| 10 | 12 | ||
| 11 | - class TapoBulb(TapoDevice, SmartBulb): | ||
| 12 | 13 | ||
| 14 | + class TapoBulb(TapoDevice, SmartBulb): | ||
| 13 | 15 | @property | |
| 14 | 16 | def is_color(self) -> bool: | |
| 15 | 17 | # TODO: this makes an assumption that only color bulbs report this | |
@@ -27,11 +29,16 @@ def is_variable_color_temp(self) -> bool: | |||
| 27 | 29 | ||
| 28 | 30 | @property | |
| 29 | 31 | def valid_temperature_range(self) -> ColorTempRange: | |
| 30 | - ct_range = self._info.get("color_temp_range") | ||
| 32 | + """Return the device-specific white temperature range (in Kelvin). | ||
| 33 | + | ||
| 34 | + :return: White temperature range in Kelvin (minimum, maximum) | ||
| 35 | + """ | ||
| 36 | + ct_range = self._info.get("color_temp_range", [0, 0]) | ||
| 31 | 37 | return ColorTempRange(min=ct_range[0], max=ct_range[1]) | |
| 32 | 38 | ||
| 33 | 39 | @property | |
| 34 | 40 | def has_effects(self) -> bool: | |
| 41 | + """Return True if the device supports effects.""" | ||
| 35 | 42 | return "dynamic_light_effect_enable" in self._info | |
| 36 | 43 | ||
| 37 | 44 | @property | |
@@ -65,40 +72,83 @@ def effect_list(self) -> Optional[List[str]]: | |||
| 65 | 72 | Example: | |
| 66 | 73 | ['Party', 'Relax', ...] | |
| 67 | 74 | """ | |
| 68 | - return AVAILABLE_EFFECTS.keys() if self.has_effects else None | ||
| 69 | - | ||
| 75 | + return list(AVAILABLE_EFFECTS.keys()) if self.has_effects else None | ||
| 70 | 76 | ||
| 71 | 77 | @property | |
| 72 | 78 | def hsv(self) -> HSV: | |
| 73 | - h, s, v = self._info.get("hue"), self._info.get("saturation"), self._info.get("brightness") | ||
| 79 | + """Return the current HSV state of the bulb. | ||
| 80 | + | ||
| 81 | + :return: hue, saturation and value (degrees, %, %) | ||
| 82 | + """ | ||
| 83 | + if not self.is_color: | ||
| 84 | + raise SmartDeviceException("Bulb does not support color.") | ||
| 85 | + | ||
| 86 | + h, s, v = ( | ||
| 87 | + self._info.get("hue", 0), | ||
| 88 | + self._info.get("saturation", 0), | ||
| 89 | + self._info.get("brightness", 0), | ||
| 90 | + ) | ||
| 74 | 91 | ||
| 75 | 92 | return HSV(hue=h, saturation=s, value=v) | |
| 76 | 93 | ||
| 77 | 94 | @property | |
| 78 | 95 | def color_temp(self) -> int: | |
| 79 | - return self._info.get("color_temp") | ||
| 96 | + """Whether the bulb supports color temperature changes.""" | ||
| 97 | + if not self.is_variable_color_temp: | ||
| 98 | + raise SmartDeviceException("Bulb does not support colortemp.") | ||
| 99 | + | ||
| 100 | + return self._info.get("color_temp", -1) | ||
| 80 | 101 | ||
| 81 | 102 | @property | |
| 82 | 103 | def brightness(self) -> int: | |
| 83 | - return self._info.get("brightness") | ||
| 104 | + """Return the current brightness in percentage.""" | ||
| 105 | + if not self.is_dimmable: # pragma: no cover | ||
| 106 | + raise SmartDeviceException("Bulb is not dimmable.") | ||
| 84 | 107 | ||
| 85 | - async def set_hsv(self, hue: int, saturation: int, value: Optional[int] = None, *, | ||
| 86 | - transition: Optional[int] = None) -> Dict: | ||
| 87 | - return await self.protocol.query({"set_device_info": { | ||
| 88 | - "hue": hue, | ||
| 89 | - "saturation": saturation, | ||
| 90 | - "brightness": value, | ||
| 91 | - }}) | ||
| 108 | + return self._info.get("brightness", -1) | ||
| 92 | 109 | ||
| 93 | - async def set_color_temp(self, temp: int, *, brightness=None, transition: Optional[int] = None) -> Dict: | ||
| 110 | + async def set_hsv( | ||
| 111 | + self, | ||
| 112 | + hue: int, | ||
| 113 | + saturation: int, | ||
| 114 | + value: Optional[int] = None, | ||
| 115 | + *, | ||
| 116 | + transition: Optional[int] = None, | ||
| 117 | + ) -> Dict: | ||
| 118 | + if not self.is_color: | ||
| 119 | + raise SmartDeviceException("Bulb does not support color.") | ||
| 120 | + | ||
| 121 | + return await self.protocol.query( | ||
| 122 | + { | ||
| 123 | + "set_device_info": { | ||
| 124 | + "hue": hue, | ||
| 125 | + "saturation": saturation, | ||
| 126 | + "brightness": value, | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + ) | ||
| 130 | + | ||
| 131 | + async def set_color_temp( | ||
| 132 | + self, temp: int, *, brightness=None, transition: Optional[int] = None | ||
| 133 | + ) -> Dict: | ||
| 94 | 134 | # TODO: Decide how to handle brightness and transition | |
| 95 | - # TODO: Note, trying to set brightness at the same time with color_temp causes error -1008 | ||
| 96 | - return await self.protocol.query({"set_device_info": {"color_temp": temp}}) | ||
| 135 | + # TODO: Note, trying to set brightness at the same time | ||
| 136 | + # with color_temp causes error -1008 | ||
| 137 | + if not self.is_variable_color_temp: | ||
| 138 | + raise SmartDeviceException("Bulb does not support colortemp.") | ||
| 97 | 139 | ||
| 140 | + return await self.protocol.query({"set_device_info": {"color_temp": temp}}) | ||
| 98 | 141 | ||
| 99 | - async def set_brightness(self, brightness: int, *, transition: Optional[int] = None) -> Dict: | ||
| 142 | + async def set_brightness( | ||
| 143 | + self, brightness: int, *, transition: Optional[int] = None | ||
| 144 | + ) -> Dict: | ||
| 100 | 145 | # TODO: Decide how to handle transitions | |
| 101 | - return await self.protocol.query({"set_device_info": {"brightness": brightness}}) | ||
| 146 | + if not self.is_dimmable: # pragma: no cover | ||
| 147 | + raise SmartDeviceException("Bulb is not dimmable.") | ||
| 148 | + | ||
| 149 | + return await self.protocol.query( | ||
| 150 | + {"set_device_info": {"brightness": brightness}} | ||
| 151 | + ) | ||
| 102 | 152 | ||
| 103 | 153 | # Default state information, should be made to settings | |
| 104 | 154 | """ | |
@@ -123,10 +173,16 @@ async def set_effect( | |||
| 123 | 173 | transition: Optional[int] = None, | |
| 124 | 174 | ) -> None: | |
| 125 | 175 | """Set an effect on the device.""" | |
| 126 | - raise NotImplementedError() # TODO: the code above does not seem to activate the effect | ||
| 127 | - return await self.protocol.query({"set_device_info": {"dynamic_light_effect_enable": 1, | ||
| 128 | - "dynamic_light_effect_id": effect}}) | ||
| 129 | - | ||
| 176 | + raise NotImplementedError() | ||
| 177 | + # TODO: the code below does to activate the effect but gives no error | ||
| 178 | + return await self.protocol.query( | ||
| 179 | + { | ||
| 180 | + "set_device_info": { | ||
| 181 | + "dynamic_light_effect_enable": 1, | ||
| 182 | + "dynamic_light_effect_id": effect, | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + ) | ||
| 130 | 186 | ||
| 131 | 187 | @property | |
| 132 | 188 | def presets(self) -> List[SmartBulbPreset]: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments