def _get_device_class(info: dict) -> Type[SmartDevice]: """Find SmartDevice subclass for device described by passed data.""" if "system" in info and "get_sysinfo" in info["system"]: sysinfo = info["system"]["get_sysinfo"] if "type" in sysinfo: type_ = sysinfo["type"] elif "mic_type" in sysinfo: type_ = sysinfo["mic_type"] else: raise SmartDeviceException( "Unable to find the device type field!") else: raise SmartDeviceException( "No 'system' nor 'get_sysinfo' in response") if ("smartlife.iot.dimmer" in info and "get_dimmer_parameters" in info["smartlife.iot.dimmer"]): return SmartDimmer elif "smartplug" in type_.lower() and "children" in sysinfo: return SmartStrip elif "smartplug" in type_.lower(): if "children" in sysinfo: return SmartStrip return SmartPlug elif "smartbulb" in type_.lower(): if "length" in sysinfo: # strips have length return SmartLightStrip return SmartBulb raise SmartDeviceException("Unknown device type: %s", type_)
def valid_temperature_range(self) -> Tuple[int, int]: """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 AZTECH_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" )
def brightness(self) -> int: """Return the current brightness in percentage.""" if not self.is_dimmable: # pragma: no cover raise SmartDeviceException("Bulb is not dimmable.") light_state = self.light_state return int(light_state["brightness"])
def color_temp(self) -> int: """Return color temperature of the device in kelvin.""" if not self.is_variable_color_temp: raise SmartDeviceException("Bulb does not support colortemp.") light_state = self.light_state return int(light_state["color_temp"])
async def set_hsv( self, hue: int, saturation: int, value: int, *, transition: int = None ) -> Dict: """Set new HSV. :param int hue: hue in degrees :param int saturation: saturation in percentage [0,100] :param int value: value in percentage [0, 100] :param int transition: transition in milliseconds. """ if not self.is_color: raise SmartDeviceException("Bulb does not support color.") if not isinstance(hue, int) or not (0 <= hue <= 360): raise ValueError( "Invalid hue value: {} " "(valid range: 0-360)".format(hue) ) if not isinstance(saturation, int) or not (0 <= saturation <= 100): raise ValueError( "Invalid saturation value: {} " "(valid range: 0-100%)".format(saturation) ) self._raise_for_invalid_brightness(value) light_state = { "hue": hue, "saturation": saturation, "brightness": value, "color_temp": 0, } return await self.set_light_state(light_state, transition=transition)
def _get_child_info(self) -> Dict: """Return the subdevice information for this device.""" for plug in self.parent.sys_info["children"]: if plug["id"] == self.child_id: return plug raise SmartDeviceException(f"Unable to find children {self.child_id}")
async def set_brightness(self, brightness: int, *, transition: int = None): """Set the new dimmer brightness level in percentage. :param int transition: transition duration in milliseconds. Using a transition will cause the dimmer to turn on. """ if not self.is_dimmable: raise SmartDeviceException("Device is not dimmable.") if not isinstance(brightness, int): raise ValueError("Brightness must be integer, " "not of %s.", type(brightness)) if not 0 <= brightness <= 100: raise ValueError("Brightness value %s is not valid." % brightness) # Dimmers do not support a brightness of 0, but bulbs do. # Coerce 0 to 1 to maintain the same interface between dimmers and bulbs. if brightness == 0: brightness = 1 if transition is not None: return await self.set_dimmer_transition(brightness, transition) return await self._query_helper(self.DIMMER_SERVICE, "set_brightness", {"brightness": brightness})
def brightness(self) -> int: """Return current brightness on dimmers. Will return a range between 0 - 100. """ if not self.is_dimmable: raise SmartDeviceException("Device is not dimmable.") sys_info = self.sys_info return int(sys_info["brightness"])
async def set_brightness(self, brightness: int, *, transition: int = None) -> Dict: """Set the brightness in percentage. :param int brightness: brightness in percent :param int transition: transition in milliseconds. """ if not self.is_dimmable: # pragma: no cover raise SmartDeviceException("Bulb is not dimmable.") self._raise_for_invalid_brightness(brightness) light_state = {"brightness": brightness} return await self.set_light_state(light_state, transition=transition)
def hsv(self) -> Tuple[int, int, int]: """Return the current HSV state of the bulb. :return: hue, saturation and value (degrees, %, %) """ if not self.is_color: raise SmartDeviceException("Bulb does not support color.") light_state = cast(dict, self.light_state) hue = light_state["hue"] saturation = light_state["saturation"] value = light_state["brightness"] return hue, saturation, value
def light_state(self) -> Dict[str, str]: """Query the light state.""" light_state = self._last_update["system"]["get_sysinfo"]["light_state"] if light_state is None: raise SmartDeviceException( "The device has no light_state or you have not called update()" ) # if the bulb is off, its state is stored under a different key # as is_on property depends on on_off itself, we check it here manually is_on = light_state["on_off"] if not is_on: off_state = {**light_state["dft_on_state"], "on_off": is_on} return cast(dict, off_state) return light_state
async def discover_single(host: str) -> SmartDevice: """Discover a single device by the given IP address. :param host: Hostname of device to query :rtype: SmartDevice :return: Object for querying/controlling found device. """ protocol = AztechSmartHomeProtocol() info = await protocol.query(host, Discover.DISCOVERY_QUERY) device_class = Discover._get_device_class(info) if device_class is not None: dev = device_class(host) await dev.update() return dev raise SmartDeviceException("Unable to discover device, received: %s" % info)
async def set_color_temp( self, temp: int, *, brightness=None, transition: int = None ) -> Dict: """Set the color temperature of the device in kelvin. :param int temp: The new color temperature, in Kelvin :param int transition: transition in milliseconds. """ if not self.is_variable_color_temp: raise SmartDeviceException("Bulb does not support colortemp.") 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 {}".format(*valid_temperature_range) ) light_state = {"color_temp": temp} if brightness is not None: light_state["brightness"] = brightness return await self.set_light_state(light_state, transition=transition)