mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
Fix warnings in our test suite (#1246)
Co-authored-by: Steven B <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
parent
157ad8e807
commit
a82ee56a27
@ -484,11 +484,11 @@ class Device(ABC):
|
||||
|
||||
_deprecated_device_type_attributes = {
|
||||
# is_type
|
||||
"is_bulb": (Module.Light, DeviceType.Bulb),
|
||||
"is_dimmer": (Module.Light, DeviceType.Dimmer),
|
||||
"is_light_strip": (Module.LightEffect, DeviceType.LightStrip),
|
||||
"is_plug": (Module.Led, DeviceType.Plug),
|
||||
"is_wallswitch": (Module.Led, DeviceType.WallSwitch),
|
||||
"is_bulb": (None, DeviceType.Bulb),
|
||||
"is_dimmer": (None, DeviceType.Dimmer),
|
||||
"is_light_strip": (None, DeviceType.LightStrip),
|
||||
"is_plug": (None, DeviceType.Plug),
|
||||
"is_wallswitch": (None, DeviceType.WallSwitch),
|
||||
"is_strip": (None, DeviceType.Strip),
|
||||
"is_strip_socket": (None, DeviceType.StripSocket),
|
||||
}
|
||||
@ -503,7 +503,9 @@ class Device(ABC):
|
||||
return None
|
||||
|
||||
for attr in attrs:
|
||||
if hasattr(check, attr):
|
||||
# Use dir() as opposed to hasattr() to avoid raising exceptions
|
||||
# from properties
|
||||
if attr in dir(check):
|
||||
return attr
|
||||
|
||||
return None
|
||||
@ -552,10 +554,7 @@ class Device(ABC):
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
# is_device_type
|
||||
if dep_device_type_attr := self._deprecated_device_type_attributes.get(name):
|
||||
module = dep_device_type_attr[0]
|
||||
msg = f"{name} is deprecated"
|
||||
if module:
|
||||
msg += f", use: {module} in device.modules instead"
|
||||
msg = f"{name} is deprecated, use device_type property instead"
|
||||
warn(msg, DeprecationWarning, stacklevel=2)
|
||||
return self.device_type == dep_device_type_attr[1]
|
||||
# Other deprecated attributes
|
||||
|
@ -33,6 +33,7 @@ class FakeSmartTransport(BaseTransport):
|
||||
warn_fixture_missing_methods=True,
|
||||
fix_incomplete_fixture_lists=True,
|
||||
is_child=False,
|
||||
get_child_fixtures=True,
|
||||
):
|
||||
super().__init__(
|
||||
config=DeviceConfig(
|
||||
@ -48,9 +49,10 @@ class FakeSmartTransport(BaseTransport):
|
||||
# child are then still reflected on the parent's lis of child device in
|
||||
if not is_child:
|
||||
self.info = copy.deepcopy(info)
|
||||
self.child_protocols = self._get_child_protocols(
|
||||
self.info, self.fixture_name, "get_child_device_list"
|
||||
)
|
||||
if get_child_fixtures:
|
||||
self.child_protocols = self._get_child_protocols(
|
||||
self.info, self.fixture_name, "get_child_device_list"
|
||||
)
|
||||
else:
|
||||
self.info = info
|
||||
if not component_nego_not_included:
|
||||
@ -220,10 +222,7 @@ class FakeSmartTransport(BaseTransport):
|
||||
"""Handle control_child command."""
|
||||
device_id = params.get("device_id")
|
||||
if device_id not in self.child_protocols:
|
||||
warn(
|
||||
f"Could not find child fixture {device_id} in {self.fixture_name}",
|
||||
stacklevel=2,
|
||||
)
|
||||
# no need to warn as the warning was raised during protocol init
|
||||
return self._handle_control_child_missing(params)
|
||||
|
||||
child_protocol: SmartProtocol = self.child_protocols[device_id]
|
||||
|
@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from kasa import Module, SmartDevice
|
||||
from kasa import Device, Module
|
||||
|
||||
from ...device_fixtures import parametrize
|
||||
|
||||
@ -16,7 +16,7 @@ contact = parametrize(
|
||||
("is_open", bool),
|
||||
],
|
||||
)
|
||||
async def test_contact_features(dev: SmartDevice, feature, type):
|
||||
async def test_contact_features(dev: Device, feature, type):
|
||||
"""Test that features are registered and work as expected."""
|
||||
contact = dev.modules.get(Module.ContactSensor)
|
||||
assert contact is not None
|
||||
|
@ -71,7 +71,7 @@ async def test_light_effect_brightness(
|
||||
|
||||
if effect_active:
|
||||
assert light_effect.is_active
|
||||
assert light_effect.brightness == dev.brightness
|
||||
assert light_effect.brightness == light_module.brightness
|
||||
|
||||
light_effect_set_brightness.assert_called_with(10)
|
||||
mock_light_effect_call.assert_called_with(
|
||||
|
@ -86,7 +86,7 @@ async def test_light_effect_brightness(
|
||||
|
||||
if effect_active:
|
||||
assert light_effect.is_active
|
||||
assert light_effect.brightness == dev.brightness
|
||||
assert light_effect.brightness == light_module.brightness
|
||||
|
||||
light_effect_set_brightness.assert_called_with(10)
|
||||
mock_light_effect_call.assert_called_with(
|
||||
|
@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from kasa import Module, SmartDevice
|
||||
from kasa import Device, Module
|
||||
|
||||
from ...device_fixtures import parametrize
|
||||
|
||||
@ -16,7 +16,7 @@ motion = parametrize(
|
||||
("motion_detected", bool),
|
||||
],
|
||||
)
|
||||
async def test_motion_features(dev: SmartDevice, feature, type):
|
||||
async def test_motion_features(dev: Device, feature, type):
|
||||
"""Test that features are registered and work as expected."""
|
||||
motion = dev.modules.get(Module.MotionSensor)
|
||||
assert motion is not None
|
||||
|
@ -13,6 +13,7 @@ from voluptuous import (
|
||||
|
||||
from kasa import Device, DeviceType, IotLightPreset, KasaException, LightState, Module
|
||||
from kasa.iot import IotBulb, IotDimmer
|
||||
from kasa.iot.modules import LightPreset as IotLightPresetModule
|
||||
|
||||
from .conftest import (
|
||||
bulb,
|
||||
@ -39,11 +40,6 @@ async def test_bulb_sysinfo(dev: Device):
|
||||
|
||||
assert dev.model is not None
|
||||
|
||||
# TODO: remove special handling for lightstrip
|
||||
if not dev.is_light_strip:
|
||||
assert dev.device_type == DeviceType.Bulb
|
||||
assert dev.is_bulb
|
||||
|
||||
|
||||
@bulb
|
||||
async def test_state_attributes(dev: Device):
|
||||
@ -88,7 +84,9 @@ async def test_hsv(dev: Device, turn_on):
|
||||
@color_bulb_iot
|
||||
async def test_set_hsv_transition(dev: IotBulb, mocker):
|
||||
set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state")
|
||||
await dev.set_hsv(10, 10, 100, transition=1000)
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await light.set_hsv(10, 10, 100, transition=1000)
|
||||
|
||||
set_light_state.assert_called_with(
|
||||
{"hue": 10, "saturation": 10, "brightness": 100, "color_temp": 0},
|
||||
@ -226,7 +224,9 @@ async def test_try_set_colortemp(dev: Device, turn_on):
|
||||
@variable_temp_iot
|
||||
async def test_set_color_temp_transition(dev: IotBulb, mocker):
|
||||
set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state")
|
||||
await dev.set_color_temp(2700, transition=100)
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await light.set_color_temp(2700, transition=100)
|
||||
|
||||
set_light_state.assert_called_with({"color_temp": 2700}, transition=100)
|
||||
|
||||
@ -234,8 +234,9 @@ async def test_set_color_temp_transition(dev: IotBulb, mocker):
|
||||
@variable_temp_iot
|
||||
async def test_unknown_temp_range(dev: IotBulb, monkeypatch, caplog):
|
||||
monkeypatch.setitem(dev._sys_info, "model", "unknown bulb")
|
||||
|
||||
assert dev.valid_temperature_range == (2700, 5000)
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
assert light.valid_temperature_range == (2700, 5000)
|
||||
assert "Unknown color temperature range, fallback to 2700-5000" in caplog.text
|
||||
|
||||
|
||||
@ -278,19 +279,21 @@ async def test_non_variable_temp(dev: Device):
|
||||
@turn_on
|
||||
async def test_dimmable_brightness(dev: IotBulb, turn_on):
|
||||
assert isinstance(dev, (IotBulb, IotDimmer))
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, turn_on)
|
||||
assert dev._is_dimmable
|
||||
|
||||
await dev.set_brightness(50)
|
||||
await light.set_brightness(50)
|
||||
await dev.update()
|
||||
assert dev.brightness == 50
|
||||
assert light.brightness == 50
|
||||
|
||||
await dev.set_brightness(10)
|
||||
await light.set_brightness(10)
|
||||
await dev.update()
|
||||
assert dev.brightness == 10
|
||||
assert light.brightness == 10
|
||||
|
||||
with pytest.raises(TypeError, match="Brightness must be an integer"):
|
||||
await dev.set_brightness("foo") # type: ignore[arg-type]
|
||||
await light.set_brightness("foo") # type: ignore[arg-type]
|
||||
|
||||
|
||||
@bulb_iot
|
||||
@ -308,7 +311,9 @@ async def test_turn_on_transition(dev: IotBulb, mocker):
|
||||
@bulb_iot
|
||||
async def test_dimmable_brightness_transition(dev: IotBulb, mocker):
|
||||
set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state")
|
||||
await dev.set_brightness(10, transition=1000)
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await light.set_brightness(10, transition=1000)
|
||||
|
||||
set_light_state.assert_called_with({"brightness": 10, "on_off": 1}, transition=1000)
|
||||
|
||||
@ -316,28 +321,30 @@ async def test_dimmable_brightness_transition(dev: IotBulb, mocker):
|
||||
@dimmable_iot
|
||||
async def test_invalid_brightness(dev: IotBulb):
|
||||
assert dev._is_dimmable
|
||||
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=re.escape("Invalid brightness value: 110 (valid range: 0-100%)"),
|
||||
):
|
||||
await dev.set_brightness(110)
|
||||
await light.set_brightness(110)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=re.escape("Invalid brightness value: -100 (valid range: 0-100%)"),
|
||||
):
|
||||
await dev.set_brightness(-100)
|
||||
await light.set_brightness(-100)
|
||||
|
||||
|
||||
@non_dimmable_iot
|
||||
async def test_non_dimmable(dev: IotBulb):
|
||||
assert not dev._is_dimmable
|
||||
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
with pytest.raises(KasaException):
|
||||
assert dev.brightness == 0
|
||||
assert light.brightness == 0
|
||||
with pytest.raises(KasaException):
|
||||
await dev.set_brightness(100)
|
||||
await light.set_brightness(100)
|
||||
|
||||
|
||||
@bulb_iot
|
||||
@ -357,7 +364,10 @@ async def test_ignore_default_not_set_without_color_mode_change_turn_on(
|
||||
|
||||
@bulb_iot
|
||||
async def test_list_presets(dev: IotBulb):
|
||||
presets = dev.presets
|
||||
light_preset = dev.modules.get(Module.LightPreset)
|
||||
assert light_preset
|
||||
assert isinstance(light_preset, IotLightPresetModule)
|
||||
presets = light_preset._deprecated_presets
|
||||
# Light strip devices may list some light effects along with normal presets but these
|
||||
# are handled by the LightEffect module so exclude preferred states with id
|
||||
raw_presets = [
|
||||
@ -376,9 +386,13 @@ async def test_list_presets(dev: IotBulb):
|
||||
@bulb_iot
|
||||
async def test_modify_preset(dev: IotBulb, mocker):
|
||||
"""Verify that modifying preset calls the and exceptions are raised properly."""
|
||||
if not dev.presets:
|
||||
if (
|
||||
not (light_preset := dev.modules.get(Module.LightPreset))
|
||||
or not light_preset._deprecated_presets
|
||||
):
|
||||
pytest.skip("Some strips do not support presets")
|
||||
|
||||
assert isinstance(light_preset, IotLightPresetModule)
|
||||
data: dict[str, int | None] = {
|
||||
"index": 0,
|
||||
"brightness": 10,
|
||||
@ -394,12 +408,12 @@ async def test_modify_preset(dev: IotBulb, mocker):
|
||||
assert preset.saturation == 0
|
||||
assert preset.color_temp == 0
|
||||
|
||||
await dev.save_preset(preset)
|
||||
await light_preset._deprecated_save_preset(preset)
|
||||
await dev.update()
|
||||
assert dev.presets[0].brightness == 10
|
||||
assert light_preset._deprecated_presets[0].brightness == 10
|
||||
|
||||
with pytest.raises(KasaException):
|
||||
await dev.save_preset(
|
||||
await light_preset._deprecated_save_preset(
|
||||
IotLightPreset(index=5, hue=0, brightness=0, saturation=0, color_temp=0) # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
@ -420,11 +434,14 @@ async def test_modify_preset(dev: IotBulb, mocker):
|
||||
)
|
||||
async def test_modify_preset_payloads(dev: IotBulb, preset, payload, mocker):
|
||||
"""Test that modify preset payloads ignore none values."""
|
||||
if not dev.presets:
|
||||
if (
|
||||
not (light_preset := dev.modules.get(Module.LightPreset))
|
||||
or not light_preset._deprecated_presets
|
||||
):
|
||||
pytest.skip("Some strips do not support presets")
|
||||
|
||||
query_helper = mocker.patch("kasa.iot.IotBulb._query_helper")
|
||||
await dev.save_preset(preset)
|
||||
await light_preset._deprecated_save_preset(preset)
|
||||
query_helper.assert_called_with(dev.LIGHT_SERVICE, "set_preferred_state", payload)
|
||||
|
||||
|
||||
@ -476,6 +493,4 @@ SYSINFO_SCHEMA_BULB = SYSINFO_SCHEMA.extend(
|
||||
|
||||
@bulb
|
||||
def test_device_type_bulb(dev: Device):
|
||||
if dev.is_light_strip:
|
||||
pytest.skip("bulb has also lightstrips to test the api")
|
||||
assert dev.device_type == DeviceType.Bulb
|
||||
assert dev.device_type in {DeviceType.Bulb, DeviceType.LightStrip}
|
||||
|
@ -6,7 +6,7 @@ import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
import sys
|
||||
from contextlib import AbstractContextManager
|
||||
from contextlib import AbstractContextManager, nullcontext
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
@ -170,15 +170,22 @@ async def _test_attribute(
|
||||
dev: Device, attribute_name, is_expected, module_name, *args, will_raise=False
|
||||
):
|
||||
if is_expected and will_raise:
|
||||
ctx: AbstractContextManager = pytest.raises(will_raise)
|
||||
ctx: AbstractContextManager | nullcontext = pytest.raises(will_raise)
|
||||
dep_context: pytest.WarningsRecorder | nullcontext = pytest.deprecated_call(
|
||||
match=(f"{attribute_name} is deprecated, use:")
|
||||
)
|
||||
elif is_expected:
|
||||
ctx = pytest.deprecated_call(match=(f"{attribute_name} is deprecated, use:"))
|
||||
ctx = nullcontext()
|
||||
dep_context = pytest.deprecated_call(
|
||||
match=(f"{attribute_name} is deprecated, use:")
|
||||
)
|
||||
else:
|
||||
ctx = pytest.raises(
|
||||
AttributeError, match=f"Device has no attribute '{attribute_name}'"
|
||||
)
|
||||
dep_context = nullcontext()
|
||||
|
||||
with ctx:
|
||||
with dep_context, ctx:
|
||||
if args:
|
||||
await getattr(dev, attribute_name)(*args)
|
||||
else:
|
||||
@ -267,16 +274,19 @@ async def test_deprecated_light_preset_attributes(dev: Device):
|
||||
await _test_attribute(dev, "presets", bool(preset), "LightPreset", will_raise=exc)
|
||||
|
||||
exc = None
|
||||
is_expected = bool(preset)
|
||||
# deprecated save_preset not implemented for smart devices as it's unlikely anyone
|
||||
# has an existing reliance on this for the newer devices.
|
||||
if not preset or isinstance(dev, SmartDevice):
|
||||
exc = AttributeError
|
||||
elif len(preset.preset_states_list) == 0:
|
||||
if isinstance(dev, SmartDevice):
|
||||
is_expected = False
|
||||
|
||||
if preset and len(preset.preset_states_list) == 0:
|
||||
exc = KasaException
|
||||
|
||||
await _test_attribute(
|
||||
dev,
|
||||
"save_preset",
|
||||
bool(preset),
|
||||
is_expected,
|
||||
"LightPreset",
|
||||
IotLightPreset(index=0, hue=100, brightness=100, saturation=0, color_temp=0), # type: ignore[call-arg]
|
||||
will_raise=exc,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from kasa import DeviceType
|
||||
from kasa import DeviceType, Module
|
||||
from kasa.iot import IotDimmer
|
||||
|
||||
from .conftest import dimmer_iot, handle_turn_on, turn_on
|
||||
@ -8,28 +8,32 @@ from .conftest import dimmer_iot, handle_turn_on, turn_on
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_brightness(dev):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, False)
|
||||
await dev.update()
|
||||
assert dev.is_on is False
|
||||
|
||||
await dev.set_brightness(99)
|
||||
await light.set_brightness(99)
|
||||
await dev.update()
|
||||
assert dev.brightness == 99
|
||||
assert light.brightness == 99
|
||||
assert dev.is_on is True
|
||||
|
||||
await dev.set_brightness(0)
|
||||
await light.set_brightness(0)
|
||||
await dev.update()
|
||||
assert dev.brightness == 99
|
||||
assert light.brightness == 99
|
||||
assert dev.is_on is False
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
@turn_on
|
||||
async def test_set_brightness_transition(dev, turn_on, mocker):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, turn_on)
|
||||
query_helper = mocker.spy(IotDimmer, "_query_helper")
|
||||
|
||||
await dev.set_brightness(99, transition=1000)
|
||||
await light.set_brightness(99, transition=1000)
|
||||
query_helper.assert_called_with(
|
||||
mocker.ANY,
|
||||
"smartlife.iot.dimmer",
|
||||
@ -37,39 +41,45 @@ async def test_set_brightness_transition(dev, turn_on, mocker):
|
||||
{"brightness": 99, "duration": 1000},
|
||||
)
|
||||
await dev.update()
|
||||
assert dev.brightness == 99
|
||||
assert light.brightness == 99
|
||||
assert dev.is_on
|
||||
|
||||
await dev.set_brightness(0, transition=1000)
|
||||
await light.set_brightness(0, transition=1000)
|
||||
await dev.update()
|
||||
assert dev.is_on is False
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_brightness_invalid(dev):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
for invalid_brightness in [-1, 101]:
|
||||
with pytest.raises(ValueError, match="Invalid brightness"):
|
||||
await dev.set_brightness(invalid_brightness)
|
||||
await light.set_brightness(invalid_brightness)
|
||||
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Brightness must be an integer"):
|
||||
await dev.set_brightness(invalid_type)
|
||||
await light.set_brightness(invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_set_brightness_invalid_transition(dev):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
for invalid_transition in [-1]:
|
||||
with pytest.raises(ValueError, match="Transition value .+? is not valid."):
|
||||
await dev.set_brightness(1, transition=invalid_transition)
|
||||
await light.set_brightness(1, transition=invalid_transition)
|
||||
for invalid_type in [0.5, "foo"]:
|
||||
with pytest.raises(TypeError, match="Transition must be integer"):
|
||||
await dev.set_brightness(1, transition=invalid_type)
|
||||
await light.set_brightness(1, transition=invalid_type)
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_turn_on_transition(dev, mocker):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
query_helper = mocker.spy(IotDimmer, "_query_helper")
|
||||
original_brightness = dev.brightness
|
||||
original_brightness = light.brightness
|
||||
|
||||
await dev.turn_on(transition=1000)
|
||||
query_helper.assert_called_with(
|
||||
@ -80,20 +90,22 @@ async def test_turn_on_transition(dev, mocker):
|
||||
)
|
||||
await dev.update()
|
||||
assert dev.is_on
|
||||
assert dev.brightness == original_brightness
|
||||
assert light.brightness == original_brightness
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_turn_off_transition(dev, mocker):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, True)
|
||||
query_helper = mocker.spy(IotDimmer, "_query_helper")
|
||||
original_brightness = dev.brightness
|
||||
original_brightness = light.brightness
|
||||
|
||||
await dev.turn_off(transition=1000)
|
||||
await dev.update()
|
||||
|
||||
assert dev.is_off
|
||||
assert dev.brightness == original_brightness
|
||||
assert light.brightness == original_brightness
|
||||
query_helper.assert_called_with(
|
||||
mocker.ANY,
|
||||
"smartlife.iot.dimmer",
|
||||
@ -105,6 +117,8 @@ async def test_turn_off_transition(dev, mocker):
|
||||
@dimmer_iot
|
||||
@turn_on
|
||||
async def test_set_dimmer_transition(dev, turn_on, mocker):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, turn_on)
|
||||
query_helper = mocker.spy(IotDimmer, "_query_helper")
|
||||
|
||||
@ -117,21 +131,23 @@ async def test_set_dimmer_transition(dev, turn_on, mocker):
|
||||
)
|
||||
await dev.update()
|
||||
assert dev.is_on
|
||||
assert dev.brightness == 99
|
||||
assert light.brightness == 99
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
@turn_on
|
||||
async def test_set_dimmer_transition_to_off(dev, turn_on, mocker):
|
||||
light = dev.modules.get(Module.Light)
|
||||
assert light
|
||||
await handle_turn_on(dev, turn_on)
|
||||
original_brightness = dev.brightness
|
||||
original_brightness = light.brightness
|
||||
query_helper = mocker.spy(IotDimmer, "_query_helper")
|
||||
|
||||
await dev.set_dimmer_transition(0, 1000)
|
||||
await dev.update()
|
||||
|
||||
assert dev.is_off
|
||||
assert dev.brightness == original_brightness
|
||||
assert light.brightness == original_brightness
|
||||
query_helper.assert_called_with(
|
||||
mocker.ANY,
|
||||
"smartlife.iot.dimmer",
|
||||
|
@ -81,14 +81,14 @@ UNSUPPORTED = {
|
||||
@wallswitch_iot
|
||||
async def test_type_detection_switch(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
assert d.is_wallswitch
|
||||
assert d.device_type == DeviceType.WallSwitch
|
||||
with pytest.deprecated_call(match="use device_type property instead"):
|
||||
assert d.is_wallswitch
|
||||
assert d.device_type is DeviceType.WallSwitch
|
||||
|
||||
|
||||
@plug_iot
|
||||
async def test_type_detection_plug(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
assert d.is_plug
|
||||
assert d.device_type == DeviceType.Plug
|
||||
|
||||
|
||||
@ -96,29 +96,26 @@ async def test_type_detection_plug(dev: Device):
|
||||
async def test_type_detection_bulb(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
# TODO: light_strip is a special case for now to force bulb tests on it
|
||||
if not d.is_light_strip:
|
||||
assert d.is_bulb
|
||||
|
||||
if d.device_type is not DeviceType.LightStrip:
|
||||
assert d.device_type == DeviceType.Bulb
|
||||
|
||||
|
||||
@strip_iot
|
||||
async def test_type_detection_strip(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
assert d.is_strip
|
||||
assert d.device_type == DeviceType.Strip
|
||||
|
||||
|
||||
@dimmer_iot
|
||||
async def test_type_detection_dimmer(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
assert d.is_dimmer
|
||||
assert d.device_type == DeviceType.Dimmer
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_type_detection_lightstrip(dev: Device):
|
||||
d = Discover._get_device_class(dev._last_update)("localhost")
|
||||
assert d.is_light_strip
|
||||
assert d.device_type == DeviceType.LightStrip
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ from voluptuous import (
|
||||
Schema,
|
||||
)
|
||||
|
||||
from kasa import Device, EmeterStatus, Module
|
||||
from kasa import Device, DeviceType, EmeterStatus, Module
|
||||
from kasa.interfaces.energy import Energy
|
||||
from kasa.iot import IotDevice, IotStrip
|
||||
from kasa.iot.modules.emeter import Emeter
|
||||
@ -61,20 +61,20 @@ async def test_get_emeter_realtime(dev):
|
||||
if not await mod._check_supported():
|
||||
pytest.skip(f"Energy module not supported for {dev}.")
|
||||
|
||||
assert dev.has_emeter
|
||||
emeter = dev.modules[Module.Energy]
|
||||
|
||||
current_emeter = await dev.get_emeter_realtime()
|
||||
current_emeter = await emeter.get_status()
|
||||
CURRENT_CONSUMPTION_SCHEMA(current_emeter)
|
||||
|
||||
|
||||
@has_emeter_iot
|
||||
@pytest.mark.requires_dummy()
|
||||
async def test_get_emeter_daily(dev):
|
||||
assert dev.has_emeter
|
||||
emeter = dev.modules[Module.Energy]
|
||||
|
||||
assert await dev.get_emeter_daily(year=1900, month=1) == {}
|
||||
assert await emeter.get_daily_stats(year=1900, month=1) == {}
|
||||
|
||||
d = await dev.get_emeter_daily()
|
||||
d = await emeter.get_daily_stats()
|
||||
assert len(d) > 0
|
||||
|
||||
k, v = d.popitem()
|
||||
@ -82,7 +82,7 @@ async def test_get_emeter_daily(dev):
|
||||
assert isinstance(v, float)
|
||||
|
||||
# Test kwh (energy, energy_wh)
|
||||
d = await dev.get_emeter_daily(kwh=False)
|
||||
d = await emeter.get_daily_stats(kwh=False)
|
||||
k2, v2 = d.popitem()
|
||||
assert v * 1000 == v2
|
||||
|
||||
@ -90,11 +90,11 @@ async def test_get_emeter_daily(dev):
|
||||
@has_emeter_iot
|
||||
@pytest.mark.requires_dummy()
|
||||
async def test_get_emeter_monthly(dev):
|
||||
assert dev.has_emeter
|
||||
emeter = dev.modules[Module.Energy]
|
||||
|
||||
assert await dev.get_emeter_monthly(year=1900) == {}
|
||||
assert await emeter.get_monthly_stats(year=1900) == {}
|
||||
|
||||
d = await dev.get_emeter_monthly()
|
||||
d = await emeter.get_monthly_stats()
|
||||
assert len(d) > 0
|
||||
|
||||
k, v = d.popitem()
|
||||
@ -102,23 +102,26 @@ async def test_get_emeter_monthly(dev):
|
||||
assert isinstance(v, float)
|
||||
|
||||
# Test kwh (energy, energy_wh)
|
||||
d = await dev.get_emeter_monthly(kwh=False)
|
||||
d = await emeter.get_monthly_stats(kwh=False)
|
||||
k2, v2 = d.popitem()
|
||||
assert v * 1000 == v2
|
||||
|
||||
|
||||
@has_emeter_iot
|
||||
async def test_emeter_status(dev):
|
||||
assert dev.has_emeter
|
||||
emeter = dev.modules[Module.Energy]
|
||||
|
||||
d = await dev.get_emeter_realtime()
|
||||
d = await emeter.get_status()
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
assert d["foo"]
|
||||
|
||||
assert d["power_mw"] == d["power"] * 1000
|
||||
# bulbs have only power according to tplink simulator.
|
||||
if not dev.is_bulb and not dev.is_light_strip:
|
||||
if (
|
||||
dev.device_type is not DeviceType.Bulb
|
||||
and dev.device_type is not DeviceType.LightStrip
|
||||
):
|
||||
assert d["voltage_mv"] == d["voltage"] * 1000
|
||||
|
||||
assert d["current_ma"] == d["current"] * 1000
|
||||
@ -128,19 +131,17 @@ async def test_emeter_status(dev):
|
||||
@pytest.mark.skip("not clearing your stats..")
|
||||
@has_emeter
|
||||
async def test_erase_emeter_stats(dev):
|
||||
assert dev.has_emeter
|
||||
emeter = dev.modules[Module.Energy]
|
||||
|
||||
await dev.erase_emeter()
|
||||
await emeter.erase_emeter()
|
||||
|
||||
|
||||
@has_emeter_iot
|
||||
async def test_current_consumption(dev):
|
||||
if dev.has_emeter:
|
||||
x = dev.current_consumption
|
||||
assert isinstance(x, float)
|
||||
assert x >= 0.0
|
||||
else:
|
||||
assert dev.current_consumption is None
|
||||
emeter = dev.modules[Module.Energy]
|
||||
x = emeter.current_consumption
|
||||
assert isinstance(x, float)
|
||||
assert x >= 0.0
|
||||
|
||||
|
||||
async def test_emeterstatus_missing_current():
|
||||
@ -180,7 +181,7 @@ async def test_emeter_daily():
|
||||
emeter_data["get_daystat"]["day_list"].append(
|
||||
{"day": now.day, "energy_wh": 500, "month": now.month, "year": now.year}
|
||||
)
|
||||
assert emeter.emeter_today == 0.500
|
||||
assert emeter.consumption_today == 0.500
|
||||
|
||||
|
||||
@has_emeter
|
||||
|
@ -16,7 +16,7 @@ from voluptuous import (
|
||||
Schema,
|
||||
)
|
||||
|
||||
from kasa import KasaException, Module
|
||||
from kasa import DeviceType, KasaException, Module
|
||||
from kasa.iot import IotDevice
|
||||
from kasa.iot.iotmodule import _merge_dict
|
||||
|
||||
@ -92,10 +92,8 @@ async def test_state_info(dev):
|
||||
@pytest.mark.requires_dummy()
|
||||
@device_iot
|
||||
async def test_invalid_connection(mocker, dev):
|
||||
with (
|
||||
mocker.patch.object(FakeIotProtocol, "query", side_effect=KasaException),
|
||||
pytest.raises(KasaException),
|
||||
):
|
||||
mocker.patch.object(FakeIotProtocol, "query", side_effect=KasaException)
|
||||
with pytest.raises(KasaException):
|
||||
await dev.update()
|
||||
|
||||
|
||||
@ -169,7 +167,7 @@ async def test_state(dev, turn_on):
|
||||
async def test_on_since(dev, turn_on):
|
||||
await handle_turn_on(dev, turn_on)
|
||||
orig_state = dev.is_on
|
||||
if "on_time" not in dev.sys_info and not dev.is_strip:
|
||||
if "on_time" not in dev.sys_info and dev.device_type is not DeviceType.Strip:
|
||||
assert dev.on_since is None
|
||||
elif orig_state:
|
||||
assert isinstance(dev.on_since, datetime)
|
||||
@ -179,7 +177,7 @@ async def test_on_since(dev, turn_on):
|
||||
|
||||
@device_iot
|
||||
async def test_time(dev):
|
||||
assert isinstance(await dev.get_time(), datetime)
|
||||
assert isinstance(dev.modules[Module.Time].time, datetime)
|
||||
|
||||
|
||||
@device_iot
|
||||
@ -216,7 +214,7 @@ async def test_representation(dev):
|
||||
@device_iot
|
||||
async def test_children(dev):
|
||||
"""Make sure that children property is exposed by every device."""
|
||||
if dev.is_strip:
|
||||
if dev.device_type is DeviceType.Strip:
|
||||
assert len(dev.children) > 0
|
||||
else:
|
||||
assert len(dev.children) == 0
|
||||
|
@ -1,35 +1,37 @@
|
||||
import pytest
|
||||
|
||||
from kasa import DeviceType
|
||||
from kasa import DeviceType, Module
|
||||
from kasa.iot import IotLightStrip
|
||||
from kasa.iot.modules import LightEffect
|
||||
|
||||
from .conftest import lightstrip_iot
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_lightstrip_length(dev: IotLightStrip):
|
||||
assert dev.is_light_strip
|
||||
assert dev.device_type == DeviceType.LightStrip
|
||||
assert dev.length == dev.sys_info["length"]
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_lightstrip_effect(dev: IotLightStrip):
|
||||
assert isinstance(dev.effect, dict)
|
||||
le: LightEffect = dev.modules[Module.LightEffect]
|
||||
assert isinstance(le._deprecated_effect, dict)
|
||||
for k in ["brightness", "custom", "enable", "id", "name"]:
|
||||
assert k in dev.effect
|
||||
assert k in le._deprecated_effect
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_effects_lightstrip_set_effect(dev: IotLightStrip):
|
||||
le: LightEffect = dev.modules[Module.LightEffect]
|
||||
with pytest.raises(
|
||||
ValueError, match="The effect Not real is not a built in effect"
|
||||
):
|
||||
await dev.set_effect("Not real")
|
||||
await le.set_effect("Not real")
|
||||
|
||||
await dev.set_effect("Candy Cane")
|
||||
await le.set_effect("Candy Cane")
|
||||
await dev.update()
|
||||
assert dev.effect["name"] == "Candy Cane"
|
||||
assert le.effect == "Candy Cane"
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
@ -38,12 +40,13 @@ async def test_effects_lightstrip_set_effect_brightness(
|
||||
dev: IotLightStrip, brightness, mocker
|
||||
):
|
||||
query_helper = mocker.patch("kasa.iot.IotLightStrip._query_helper")
|
||||
le: LightEffect = dev.modules[Module.LightEffect]
|
||||
|
||||
# test that default brightness works (100 for candy cane)
|
||||
if brightness == 100:
|
||||
await dev.set_effect("Candy Cane")
|
||||
await le.set_effect("Candy Cane")
|
||||
else:
|
||||
await dev.set_effect("Candy Cane", brightness=brightness)
|
||||
await le.set_effect("Candy Cane", brightness=brightness)
|
||||
|
||||
args, kwargs = query_helper.call_args_list[0]
|
||||
payload = args[2]
|
||||
@ -56,12 +59,13 @@ async def test_effects_lightstrip_set_effect_transition(
|
||||
dev: IotLightStrip, transition, mocker
|
||||
):
|
||||
query_helper = mocker.patch("kasa.iot.IotLightStrip._query_helper")
|
||||
le: LightEffect = dev.modules[Module.LightEffect]
|
||||
|
||||
# test that default (500 for candy cane) transition works
|
||||
if transition == 500:
|
||||
await dev.set_effect("Candy Cane")
|
||||
await le.set_effect("Candy Cane")
|
||||
else:
|
||||
await dev.set_effect("Candy Cane", transition=transition)
|
||||
await le.set_effect("Candy Cane", transition=transition)
|
||||
|
||||
args, kwargs = query_helper.call_args_list[0]
|
||||
payload = args[2]
|
||||
@ -70,8 +74,9 @@ async def test_effects_lightstrip_set_effect_transition(
|
||||
|
||||
@lightstrip_iot
|
||||
async def test_effects_lightstrip_has_effects(dev: IotLightStrip):
|
||||
assert dev.has_effects is True
|
||||
assert dev.effect_list
|
||||
le: LightEffect = dev.modules[Module.LightEffect]
|
||||
assert le is not None
|
||||
assert le.effect_list
|
||||
|
||||
|
||||
@lightstrip_iot
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from kasa import DeviceType
|
||||
|
||||
from .conftest import plug, plug_iot, plug_smart, switch_smart, wallswitch_iot
|
||||
@ -16,7 +18,6 @@ async def test_plug_sysinfo(dev):
|
||||
assert dev.model is not None
|
||||
|
||||
assert dev.device_type == DeviceType.Plug or dev.device_type == DeviceType.Strip
|
||||
assert dev.is_plug or dev.is_strip
|
||||
|
||||
|
||||
@wallswitch_iot
|
||||
@ -27,37 +28,38 @@ async def test_switch_sysinfo(dev):
|
||||
assert dev.model is not None
|
||||
|
||||
assert dev.device_type == DeviceType.WallSwitch
|
||||
assert dev.is_wallswitch
|
||||
|
||||
|
||||
@plug_iot
|
||||
async def test_plug_led(dev):
|
||||
original = dev.led
|
||||
with pytest.deprecated_call(match="use: Module.Led in device.modules instead"):
|
||||
original = dev.led
|
||||
|
||||
await dev.set_led(False)
|
||||
await dev.update()
|
||||
assert not dev.led
|
||||
await dev.set_led(False)
|
||||
await dev.update()
|
||||
assert not dev.led
|
||||
|
||||
await dev.set_led(True)
|
||||
await dev.update()
|
||||
assert dev.led
|
||||
await dev.set_led(True)
|
||||
await dev.update()
|
||||
assert dev.led
|
||||
|
||||
await dev.set_led(original)
|
||||
await dev.set_led(original)
|
||||
|
||||
|
||||
@wallswitch_iot
|
||||
async def test_switch_led(dev):
|
||||
original = dev.led
|
||||
with pytest.deprecated_call(match="use: Module.Led in device.modules instead"):
|
||||
original = dev.led
|
||||
|
||||
await dev.set_led(False)
|
||||
await dev.update()
|
||||
assert not dev.led
|
||||
await dev.set_led(False)
|
||||
await dev.update()
|
||||
assert not dev.led
|
||||
|
||||
await dev.set_led(True)
|
||||
await dev.update()
|
||||
assert dev.led
|
||||
await dev.set_led(True)
|
||||
await dev.update()
|
||||
assert dev.led
|
||||
|
||||
await dev.set_led(original)
|
||||
await dev.set_led(original)
|
||||
|
||||
|
||||
@plug_smart
|
||||
|
@ -45,10 +45,8 @@ async def test_update_no_device_info(dev: SmartDevice, mocker: MockerFixture):
|
||||
"get_device_time": {},
|
||||
}
|
||||
msg = f"get_device_info not found in {mock_response} for device 127.0.0.123"
|
||||
with (
|
||||
mocker.patch.object(dev.protocol, "query", return_value=mock_response),
|
||||
pytest.raises(KasaException, match=msg),
|
||||
):
|
||||
mocker.patch.object(dev.protocol, "query", return_value=mock_response)
|
||||
with pytest.raises(KasaException, match=msg):
|
||||
await dev.update()
|
||||
|
||||
|
||||
|
@ -325,6 +325,7 @@ async def test_smart_protocol_lists_single_request(mocker, list_sum, batch_size)
|
||||
"foobar",
|
||||
list_return_size=batch_size,
|
||||
component_nego_not_included=True,
|
||||
get_child_fixtures=False,
|
||||
)
|
||||
protocol = SmartProtocol(transport=ft)
|
||||
query_spy = mocker.spy(protocol, "_execute_query")
|
||||
@ -357,6 +358,7 @@ async def test_smart_protocol_lists_multiple_request(mocker, list_sum, batch_siz
|
||||
"foobar",
|
||||
list_return_size=batch_size,
|
||||
component_nego_not_included=True,
|
||||
get_child_fixtures=False,
|
||||
)
|
||||
protocol = SmartProtocol(transport=ft)
|
||||
query_spy = mocker.spy(protocol, "_execute_query")
|
||||
|
Loading…
Reference in New Issue
Block a user