Esempio n. 1
0
    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():
            return SmartPlug
        elif "smartbulb" in type_.lower():
            return SmartBulb

        raise SmartDeviceException("Unknown device type: %s", type_)
Esempio n. 2
0
    def _get_device_class(info: dict) -> Type[SmartDevice]:
        """Find SmartDevice subclass for device described by passed data."""
        if "system" not in info or "get_sysinfo" not in info["system"]:
            raise SmartDeviceException(
                "No 'system' or 'get_sysinfo' in response")

        sysinfo = info["system"]["get_sysinfo"]
        type_ = sysinfo.get("type", sysinfo.get("mic_type"))
        if type_ is None:
            raise SmartDeviceException("Unable to find the device type field!")

        if "dev_name" in sysinfo and "Dimmer" in sysinfo["dev_name"]:
            return SmartDimmer

        if "smartplug" in type_.lower():
            if "children" in sysinfo:
                return SmartStrip

            return SmartPlug

        if "smartbulb" in type_.lower():
            if "length" in sysinfo:  # strips have length
                return SmartLightStrip

            return SmartBulb

        raise SmartDeviceException("Unknown device type: %s" % type_)
Esempio n. 3
0
    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 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")
Esempio n. 4
0
    def _get_new_device_class(info: dict) -> Type[SmartDevice]:
        """Find SmartDevice subclass given new discovery payload."""
        if "result" not in info:
            raise SmartDeviceException("No 'result' in discovery response")

        if "device_type" not in info["result"]:
            raise SmartDeviceException("No 'device_type' in discovery result")

        dtype = info["result"]["device_type"]

        if dtype == "IOT.SMARTPLUGSWITCH":
            return SmartPlug

        raise SmartDeviceException("Unknown device type: %s", dtype)
Esempio n. 5
0
    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"])
Esempio n. 6
0
    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"])
Esempio n. 7
0
    async def get_emeter_realtime(self) -> EmeterStatus:
        """Retrieve current energy readings."""
        if not self.has_emeter:
            raise SmartDeviceException("Device has no emeter")

        plug_emeter = False
        for plug in self.children:
            if plug.has_emeter:
                plug_emeter = True

        if not plug_emeter:
            return EmeterStatus(await self._query_helper(self.emeter_type, "get_realtime"))

        emeter_rt: DefaultDict[int, float] = defaultdict(lambda: 0.0)
        count = 0
        for plug in self.children:
            if not plug.has_emeter:
                continue
            count += 1
            plug_emeter_rt = await plug.get_emeter_realtime()
            for field, value in plug_emeter_rt.items():
                emeter_rt[field] += value
    
        # Voltage is averaged
        emeter_rt['voltage_mv'] /= count

        return EmeterStatus(emeter_rt)
Esempio n. 8
0
    def get_plug_by_name(self, name: str) -> "SmartStripPlug":
        """Return child plug for given name."""
        for p in self.plugs:
            if p.alias == name:
                return p

        raise SmartDeviceException(f"Device has no child with {name}")
Esempio n. 9
0
    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}")
Esempio n. 10
0
    async def set_hsv(self, hue: int, saturation: int, value: int):
        """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]
        """
        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,
        }
        await self.set_light_state(light_state)
Esempio n. 11
0
    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})
Esempio n. 12
0
    def _get_new_owner(info: dict) -> Optional[str]:
        """Find owner given new-style discovery payload."""
        if "result" not in info:
            raise SmartDeviceException("No 'result' in discovery response")

        if "owner" not in info["result"]:
            return None

        return info["result"]["owner"]
Esempio n. 13
0
    async def set_brightness(self, brightness: int) -> None:
        """Set the brightness in percentage."""
        if not self.is_dimmable:  # pragma: no cover
            raise SmartDeviceException("Bulb is not dimmable.")

        self._raise_for_invalid_brightness(brightness)

        light_state = {"brightness": brightness}
        await self.set_light_state(light_state)
Esempio n. 14
0
    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"])
Esempio n. 15
0
    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)
Esempio n. 16
0
    def color_temp(self) -> int:
        """Return color temperature of the device.

        :return: Color temperature in Kelvin
        :rtype: int
        """
        if not self.is_variable_color_temp:
            raise SmartDeviceException("Bulb does not support colortemp.")

        light_state = self.light_state
        if not self.is_on:
            return int(light_state["dft_on_state"]["color_temp"])
        else:
            return int(light_state["color_temp"])
Esempio n. 17
0
    def brightness(self) -> int:
        """Return the current brightness.

        :return: brightness in percent
        :rtype: int
        """
        if not self.is_dimmable:  # pragma: no cover
            raise SmartDeviceException("Bulb is not dimmable.")

        light_state = self.light_state
        if not self.is_on:
            return int(light_state["dft_on_state"]["brightness"])
        else:
            return int(light_state["brightness"])
Esempio n. 18
0
    async def set_brightness(self, value: int):
        """Set the new dimmer brightness level in percentage."""
        if not self.is_dimmable:
            raise SmartDeviceException("Device is not dimmable.")

        if not isinstance(value, int):
            raise ValueError("Brightness must be integer, "
                             "not of %s.", type(value))
        elif 0 <= value <= 100:
            return await self._query_helper("smartlife.iot.dimmer",
                                            "set_brightness",
                                            {"brightness": value})
        else:
            raise ValueError("Brightness value %s is not valid." % value)
Esempio n. 19
0
    async def set_color_temp(self, temp: int) -> None:
        """Set the color temperature of the device in kelvin."""
        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}
        await self.set_light_state(light_state)
Esempio n. 20
0
    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
Esempio n. 21
0
    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
Esempio n. 22
0
    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 = TPLinkSmartHomeProtocol()

        info = await protocol.query(host, Discover.DISCOVERY_QUERY)

        device_class = Discover._get_device_class(info)
        if device_class is not None:
            return device_class(host)

        raise SmartDeviceException("Unable to discover device, received: %s" %
                                   info)
Esempio n. 23
0
    async def set_brightness(self, value: int):
        """Set the new dimmer brightness level.

        Note:
        When setting brightness, if the light is not
        already on, it will be turned on automatically.

        :param value: integer between 1 and 100

        """
        if not self.is_dimmable:
            raise SmartDeviceException("Device is not dimmable.")

        if not isinstance(value, int):
            raise ValueError("Brightness must be integer, "
                             "not of %s.", type(value))
        elif 0 < value <= 100:
            await self.turn_on()
            await self._query_helper("smartlife.iot.dimmer", "set_brightness",
                                     {"brightness": value})
            await self.update()
        else:
            raise ValueError("Brightness value %s is not valid." % value)
Esempio n. 24
0
    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)
Esempio n. 25
0
    async def discover_single(host: str, complete: bool) -> SmartDevice:
        """Discover a single device by the given IP address.

        :param host: Hostname of device to query
        :param complete: Whether to discover only with get_sysinfo or all options
        :rtype: SmartDevice
        :return: Object for querying/controlling found device.
        """
        protocol = TPLinkSmartHomeProtocol()

        if complete:
            info = await protocol.query(host,
                                        Discover.COMPLETE_DISCOVERY_QUERY)
        else:
            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)
Esempio n. 26
0
 def get_plug_by_index(self, index: int) -> "SmartStripPlug":
     """Return child plug for given index."""
     if index + 1 > len(self.plugs) or index < 0:
         raise SmartDeviceException(
             f"Invalid index {index}, device has {len(self.plugs)} plugs")
     return self.plugs[index]