Пример #1
0
    def set_hsv(self, state: Tuple[int, int, int], duration=1):
        """
        Sets new HSV, if supported

        :param tuple state: hue, saturation and value (degrees, %, %)
        """
        if not self.is_color:
            return None

        if not isinstance(state[0], int) or not (0 <= state[0] <= 360):
            raise SmartDeviceException('Invalid hue value: {} '
                                       '(valid range: 0-360)'.format(state[0]))

        if not isinstance(state[1], int) or not (0 <= state[1] <= 100):
            raise SmartDeviceException('Invalid saturation value: {} '
                                       '(valid range: 0-100%)'.format(
                                           state[1]))

        if not isinstance(state[2], int) or not (0 <= state[2] <= 100):
            raise SmartDeviceException('Invalid brightness value: {} '
                                       '(valid range: 0-100%)'.format(
                                           state[2]))

        light_state = {
            "hue": state[0],
            "saturation": state[1],
            "brightness": state[2],
            "color_temp": 0,
            "ignore_default": 1,
            "transition_period": max(min(int(duration * 1000), 10000), 0)
        }
        self.set_light_state(light_state)
Пример #2
0
    def hsv(self, state: Tuple[int, int, int]):
        """
        Sets new HSV, if supported

        :param tuple state: hue, saturation and value (degrees, %, %)
        """
        if not self.is_color:
            return None

        if not isinstance(state[0], int) or not (0 <= state[0] <= 360):
            raise SmartDeviceException(
                    'Invalid hue value: {} '
                    '(valid range: 0-360)'.format(state[0]))

        if not isinstance(state[1], int) or not (0 <= state[1] <= 100):
            raise SmartDeviceException(
                    'Invalid saturation value: {} '
                    '(valid range: 0-100%)'.format(state[1]))

        if not isinstance(state[2], int) or not (0 <= state[2] <= 100):
            raise SmartDeviceException(
                    'Invalid brightness value: {} '
                    '(valid range: 0-100%)'.format(state[2]))

        light_state = {
            "hue": state[0],
            "saturation": state[1],
            "brightness": state[2],
            "color_temp": 0
            }
        self.set_light_state(light_state)
Пример #3
0
    def set_hsv(self, hue: int, saturation: int, value: int):
        """Set new HSV.

        :param tuple state: hue, saturation and value (degrees, %, %)
        """
        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,
        }
        self.set_light_state(light_state)
Пример #4
0
async def test_async_add_entities_retry_cancel(hass: HomeAssistantType):
    """Test interval callback."""
    async_add_entities_callback = MagicMock()

    callback_side_effects = [
        False,
        False,
        True,  # Object 1
        False,
        True,  # Object 2
        SmartDeviceException("My error"),
        False,
        True,  # Object 3
        True,  # Object 4
    ]

    callback = MagicMock(side_effect=callback_side_effects)

    objects = ["Object 1", "Object 2", "Object 3", "Object 4"]
    cancel = await async_add_entities_retry(
        hass,
        async_add_entities_callback,
        objects,
        callback,
        interval=timedelta(milliseconds=100),
    )
    cancel()
    await hass.async_block_till_done()

    assert callback.call_count == 4
Пример #5
0
    def set_light_state_side_effect(state_data: dict):
        nonlocal set_state_call_count, light_mock_data
        set_state_call_count += 1

        if set_state_call_count == 1:
            raise SmartDeviceException()

        return light_mock_data.set_light_state(state_data)
Пример #6
0
    def get_light_state_side_effect():
        nonlocal get_state_call_count
        get_state_call_count += 1

        if get_state_call_count == 1:
            raise SmartDeviceException()

        return light_mock_data.light_state
Пример #7
0
    def get_sysinfo_side_effect():
        nonlocal get_sysinfo_call_count
        get_sysinfo_call_count += 1

        # Need to fail on the 2nd call because the first call is used to
        # determine if the device is online during the light platform's
        # setup hook.
        if get_sysinfo_call_count == 2:
            raise SmartDeviceException()

        return light_mock_data.sys_info
Пример #8
0
    def set_brightness(self, brightness: int) -> None:
        """Set the current brightness of the device.

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

        self._raise_for_invalid_brightness(brightness)

        light_state = {"brightness": brightness}
        self.set_light_state(light_state)
Пример #9
0
    def transition_period(self, period: int):
        """Set default transition period.

        :param int period: transition period in milliseconds, default 500
        """

        if period < 0:
            raise SmartDeviceException(
                'Invalid transition value: {}'
                '(requires positive int value)'.format(period))

        self._transition_period = period
Пример #10
0
    def brightness(self) -> int:
        """Return current brightness on dimmers.

        Will return a range between 0 - 100.

        :returns: integer
        :rtype: int
        """
        if not self.is_dimmable:
            raise SmartDeviceException("Device is not dimmable.")

        return int(self.sys_info["brightness"])
Пример #11
0
    def brightness(self) -> int:
        """Current brightness of the device.

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

        light_state = self.get_light_state()
        if not self.is_on:
            return int(light_state["dft_on_state"]["brightness"])
        else:
            return int(light_state["brightness"])
Пример #12
0
    def _get_device_class(info: dict) -> Optional[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 "smartplug" in type_.lower() and "children" in sysinfo:
            return SmartStrip
        elif "smartplug" in type_.lower():
            return SmartPlug
        elif "smartbulb" in type_.lower():
            return SmartBulb

        return None
Пример #13
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.get_light_state()
        if not self.is_on:
            return int(light_state["dft_on_state"]["color_temp"])
        else:
            return int(light_state["color_temp"])
Пример #14
0
    def set_color_temp(self, temp: int) -> None:
        """Set the color temperature of the device.

        :param int temp: The new color temperature, in Kelvin
        """
        if not self.is_variable_color_temp:
            raise SmartDeviceException("Bulb does not support colortemp.")

        if (temp < self.valid_temperature_range[0]
                or temp > self.valid_temperature_range[1]):
            raise ValueError("Temperature should be between {} "
                             "and {}".format(*self.valid_temperature_range))

        light_state = {"color_temp": temp}
        self.set_light_state(light_state)
Пример #15
0
    def set_brightness(self, brightness: int, period: int = -1) -> None:
        """Set the current brightness of the device.

        :param int brightness: brightness in percent
        :param int period: transition period in milliseconds
        """
        if not self.is_dimmable:  # pragma: no cover
            raise SmartDeviceException("Bulb is not dimmable.")

        self._raise_for_invalid_brightness(brightness)

        if period < 0:
            period = self._transition_period

        light_state = {"brightness": brightness, "transition_period": period}
        self.set_light_state(light_state)
Пример #16
0
async def test_configuring_devices_from_multiple_sources(hass):
    """Test static and discover devices are not duplicated."""
    with patch(
            "homeassistant.components.tplink.common.Discover.discover"
    ) as discover, patch(
            "homeassistant.components.tplink.common.SmartDevice._query_helper"
    ), patch(
            "homeassistant.config_entries.ConfigEntries.async_forward_entry_setup"
    ):
        discover_device_fail = SmartPlug("123.123.123.123")
        discover_device_fail.get_sysinfo = MagicMock(
            side_effect=SmartDeviceException())

        discover.return_value = {
            "123.123.123.1": SmartBulb("123.123.123.1"),
            "123.123.123.2": SmartPlug("123.123.123.2"),
            "123.123.123.3": SmartBulb("123.123.123.3"),
            "123.123.123.4": SmartPlug("123.123.123.4"),
            "123.123.123.123": discover_device_fail,
            "123.123.123.124": UnknownSmartDevice("123.123.123.124"),
        }

        await async_setup_component(
            hass,
            tplink.DOMAIN,
            {
                tplink.DOMAIN: {
                    CONF_LIGHT: [{
                        CONF_HOST: "123.123.123.1"
                    }],
                    CONF_SWITCH: [{
                        CONF_HOST: "123.123.123.2"
                    }],
                    CONF_DIMMER: [{
                        CONF_HOST: "123.123.123.22"
                    }],
                }
            },
        )
        await hass.async_block_till_done()

        assert len(discover.mock_calls) == 1
        assert len(hass.data[tplink.DOMAIN][CONF_LIGHT]) == 3
        assert len(hass.data[tplink.DOMAIN][CONF_SWITCH]) == 2
Пример #17
0
async def test_async_add_entities_retry(hass: HomeAssistantType):
    """Test interval callback."""
    async_add_entities_callback = MagicMock()

    # The objects that will be passed to async_add_entities_callback.
    objects = [
        "Object 1",
        "Object 2",
        "Object 3",
        "Object 4",
    ]

    # For each call to async_add_entities_callback, the following side effects
    # will be triggered in order. This set of side effects accuratley simulates
    # 3 attempts to add all entities while also handling several return types.
    # To help understand what's going on, a comment exists describing what the
    # object list looks like throughout the iterations.
    callback_side_effects = [
        # OB1, OB2, OB3, OB4
        False,
        False,
        True,  # Object 3
        False,

        # OB1, OB2, OB4
        True,  # Object 1
        SmartDeviceException("My error"),
        False,

        # OB2, OB4
        True,  # Object 2
        True,  # Object 4
    ]

    callback = MagicMock(side_effect=callback_side_effects)

    await async_add_entities_retry(hass,
                                   async_add_entities_callback,
                                   objects,
                                   callback,
                                   interval=timedelta(milliseconds=100))
    await hass.async_block_till_done()

    assert callback.call_count == len(callback_side_effects)
Пример #18
0
    def set_color_temp(self, temp: int, period: int = -1) -> None:
        """Set the color temperature of the device.

        :param int temp: The new color temperature, in Kelvin
        :param int period: transition period in milliseconds
        """
        if not self.is_variable_color_temp:
            raise SmartDeviceException("Bulb does not support colortemp.")

        if (temp < self.valid_temperature_range[0]
                or temp > self.valid_temperature_range[1]):
            raise ValueError("Temperature should be between {} "
                             "and {}".format(*self.valid_temperature_range))

        if period < 0:
            period = self._transition_period

        light_state = {"color_temp": temp, "transition_period": period}
        self.set_light_state(light_state)
Пример #19
0
    def hsv(self) -> Tuple[int, int, int]:
        """Return the current HSV state of the bulb.

        :return: hue, saturation and value (degrees, %, %)
        :rtype: tuple
        """

        if not self.is_color:
            raise SmartDeviceException("Bulb does not support color.")

        light_state = self.get_light_state()
        if not self.is_on:
            hue = light_state["dft_on_state"]["hue"]
            saturation = light_state["dft_on_state"]["saturation"]
            value = light_state["dft_on_state"]["brightness"]
        else:
            hue = light_state["hue"]
            saturation = light_state["saturation"]
            value = light_state["brightness"]

        return hue, saturation, value
Пример #20
0
    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:
            self.turn_on()
            self._query_helper("smartlife.iot.dimmer", "set_brightness",
                               {"brightness": value})
        else:
            raise ValueError("Brightness value %s is not valid." % value)
Пример #21
0
async def test_not_ready(hass: HomeAssistant):
    """Test for not ready when configured devices are not available."""
    config = {
        tplink.DOMAIN: {
            CONF_DISCOVERY: False,
            CONF_SWITCH: [{
                CONF_HOST: "321.321.321.321"
            }],
        }
    }

    with patch(
            "homeassistant.components.tplink.common.Discover.discover"
    ), patch(
            "homeassistant.components.tplink.get_static_devices"
    ) as get_static_devices, patch(
            "homeassistant.components.tplink.common.SmartDevice._query_helper"
    ), patch(
            "homeassistant.components.tplink.light.async_setup_entry",
            return_value=mock_coro(True),
    ), patch(
            "homeassistant.components.tplink.switch.async_setup_entry",
            return_value=mock_coro(True),
    ), patch("homeassistant.components.tplink.common.SmartPlug.is_dimmable",
             False):

        switch = SmartPlug("321.321.321.321")
        switch.get_sysinfo = MagicMock(side_effect=SmartDeviceException())
        get_static_devices.return_value = SmartDevices([], [switch])

        await async_setup_component(hass, tplink.DOMAIN, config)
        await hass.async_block_till_done()

        entries = hass.config_entries.async_entries(tplink.DOMAIN)

        assert len(entries) == 1
        assert entries[0].state is config_entries.ConfigEntryState.SETUP_RETRY
Пример #22
0
async def test_not_available_at_startup(hass: HomeAssistant):
    """Test when configured devices are not available."""
    config = {
        tplink.DOMAIN: {
            CONF_DISCOVERY: False,
            CONF_SWITCH: [{
                CONF_HOST: "321.321.321.321"
            }],
        }
    }

    with patch(
            "homeassistant.components.tplink.common.Discover.discover"
    ), patch(
            "homeassistant.components.tplink.get_static_devices"
    ) as get_static_devices, patch(
            "homeassistant.components.tplink.common.SmartDevice._query_helper"
    ), patch(
            "homeassistant.components.tplink.light.async_setup_entry",
            return_value=mock_coro(True),
    ), patch("homeassistant.components.tplink.common.SmartPlug.is_dimmable",
             False):

        switch = SmartPlug("321.321.321.321")
        switch.get_sysinfo = MagicMock(side_effect=SmartDeviceException())
        get_static_devices.return_value = SmartDevices([], [switch])

        # run setup while device unreachable
        await async_setup_component(hass, tplink.DOMAIN, config)
        await hass.async_block_till_done()

        entries = hass.config_entries.async_entries(tplink.DOMAIN)
        assert len(entries) == 1
        assert entries[0].state is config_entries.ConfigEntryState.LOADED

        entities = hass.states.async_entity_ids(SWITCH_DOMAIN)
        assert len(entities) == 0

        # retrying with still unreachable device
        async_fire_time_changed(hass, dt.utcnow() + UNAVAILABLE_RETRY_DELAY)
        await hass.async_block_till_done()

        entries = hass.config_entries.async_entries(tplink.DOMAIN)
        assert len(entries) == 1
        assert entries[0].state is config_entries.ConfigEntryState.LOADED

        entities = hass.states.async_entity_ids(SWITCH_DOMAIN)
        assert len(entities) == 0

        # retrying with now reachable device
        switch.get_sysinfo = MagicMock(
            return_value=SMARTPLUG_HS100_DATA["sysinfo"])
        async_fire_time_changed(hass, dt.utcnow() + UNAVAILABLE_RETRY_DELAY)
        await hass.async_block_till_done()

        entries = hass.config_entries.async_entries(tplink.DOMAIN)
        assert len(entries) == 1
        assert entries[0].state is config_entries.ConfigEntryState.LOADED

        entities = hass.states.async_entity_ids(SWITCH_DOMAIN)
        assert len(entities) == 1