2024-03-15 15:55:48 +00:00
|
|
|
"""Tests for SMART devices."""
|
2024-04-16 18:21:20 +00:00
|
|
|
|
2024-02-15 18:10:34 +00:00
|
|
|
import logging
|
2024-03-15 16:18:13 +00:00
|
|
|
from typing import Any, Dict
|
2020-05-27 14:55:18 +00:00
|
|
|
|
2024-03-15 16:18:13 +00:00
|
|
|
import pytest
|
|
|
|
from pytest_mock import MockerFixture
|
2020-05-27 14:55:18 +00:00
|
|
|
|
2024-03-15 15:55:48 +00:00
|
|
|
from kasa import KasaException
|
2024-02-15 18:10:34 +00:00
|
|
|
from kasa.exceptions import SmartErrorCode
|
2024-04-17 11:33:10 +00:00
|
|
|
from kasa.smart import SmartBulb, SmartDevice
|
2020-05-27 14:55:18 +00:00
|
|
|
|
2024-02-15 18:10:34 +00:00
|
|
|
from .conftest import (
|
2024-04-17 11:33:10 +00:00
|
|
|
bulb_smart,
|
2024-02-15 18:10:34 +00:00
|
|
|
device_smart,
|
2024-02-04 15:20:08 +00:00
|
|
|
)
|
2023-09-13 13:46:38 +00:00
|
|
|
|
2020-05-27 14:55:18 +00:00
|
|
|
|
2024-02-15 18:10:34 +00:00
|
|
|
@device_smart
|
2024-02-19 17:01:31 +00:00
|
|
|
async def test_try_get_response(dev: SmartDevice, caplog):
|
2024-02-15 18:10:34 +00:00
|
|
|
mock_response: dict = {
|
2024-02-19 17:01:31 +00:00
|
|
|
"get_device_info": SmartErrorCode.PARAMS_ERROR,
|
2024-02-15 18:10:34 +00:00
|
|
|
}
|
|
|
|
caplog.set_level(logging.DEBUG)
|
2024-02-19 17:01:31 +00:00
|
|
|
dev._try_get_response(mock_response, "get_device_info", {})
|
|
|
|
msg = "Error PARAMS_ERROR(-1008) getting request get_device_info for device 127.0.0.123"
|
2024-02-15 18:10:34 +00:00
|
|
|
assert msg in caplog.text
|
|
|
|
|
|
|
|
|
|
|
|
@device_smart
|
2024-03-15 16:18:13 +00:00
|
|
|
async def test_update_no_device_info(dev: SmartDevice, mocker: MockerFixture):
|
2024-02-15 18:10:34 +00:00
|
|
|
mock_response: dict = {
|
|
|
|
"get_device_usage": {},
|
|
|
|
"get_device_time": {},
|
|
|
|
}
|
|
|
|
msg = f"get_device_info not found in {mock_response} for device 127.0.0.123"
|
2024-03-15 16:18:13 +00:00
|
|
|
with mocker.patch.object(
|
|
|
|
dev.protocol, "query", return_value=mock_response
|
|
|
|
), pytest.raises(KasaException, match=msg):
|
2024-02-15 18:10:34 +00:00
|
|
|
await dev.update()
|
2024-03-15 16:18:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
@device_smart
|
|
|
|
async def test_initial_update(dev: SmartDevice, mocker: MockerFixture):
|
|
|
|
"""Test the initial update cycle."""
|
|
|
|
# As the fixture data is already initialized, we reset the state for testing
|
|
|
|
dev._components_raw = None
|
|
|
|
dev._features = {}
|
|
|
|
|
|
|
|
negotiate = mocker.spy(dev, "_negotiate")
|
|
|
|
initialize_modules = mocker.spy(dev, "_initialize_modules")
|
|
|
|
initialize_features = mocker.spy(dev, "_initialize_features")
|
|
|
|
|
|
|
|
# Perform two updates and verify that initialization is only done once
|
|
|
|
await dev.update()
|
|
|
|
await dev.update()
|
|
|
|
|
|
|
|
negotiate.assert_called_once()
|
|
|
|
assert dev._components_raw is not None
|
|
|
|
initialize_modules.assert_called_once()
|
|
|
|
assert dev.modules
|
|
|
|
initialize_features.assert_called_once()
|
|
|
|
assert dev.features
|
|
|
|
|
|
|
|
|
|
|
|
@device_smart
|
|
|
|
async def test_negotiate(dev: SmartDevice, mocker: MockerFixture):
|
|
|
|
"""Test that the initial negotiation performs expected steps."""
|
|
|
|
# As the fixture data is already initialized, we reset the state for testing
|
|
|
|
dev._components_raw = None
|
|
|
|
dev._children = {}
|
|
|
|
|
|
|
|
query = mocker.spy(dev.protocol, "query")
|
|
|
|
initialize_children = mocker.spy(dev, "_initialize_children")
|
|
|
|
await dev._negotiate()
|
|
|
|
|
|
|
|
# Check that we got the initial negotiation call
|
|
|
|
query.assert_any_call({"component_nego": None, "get_device_info": None})
|
|
|
|
assert dev._components_raw
|
|
|
|
|
|
|
|
# Check the children are created, if device supports them
|
|
|
|
if "child_device" in dev._components:
|
|
|
|
initialize_children.assert_called_once()
|
|
|
|
query.assert_any_call(
|
|
|
|
{
|
|
|
|
"get_child_device_component_list": None,
|
|
|
|
"get_child_device_list": None,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
assert len(dev.children) == dev.internal_state["get_child_device_list"]["sum"]
|
|
|
|
|
|
|
|
|
|
|
|
@device_smart
|
|
|
|
async def test_update_module_queries(dev: SmartDevice, mocker: MockerFixture):
|
|
|
|
"""Test that the regular update uses queries from all supported modules."""
|
|
|
|
query = mocker.spy(dev.protocol, "query")
|
|
|
|
|
|
|
|
# We need to have some modules initialized by now
|
|
|
|
assert dev.modules
|
|
|
|
|
|
|
|
await dev.update()
|
|
|
|
full_query: Dict[str, Any] = {}
|
|
|
|
for mod in dev.modules.values():
|
2024-03-28 11:38:58 +00:00
|
|
|
full_query = {**full_query, **mod.query()}
|
2024-03-15 16:18:13 +00:00
|
|
|
|
|
|
|
query.assert_called_with(full_query)
|
2024-04-17 11:33:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
@bulb_smart
|
|
|
|
async def test_smartdevice_brightness(dev: SmartBulb):
|
|
|
|
"""Test brightness setter and getter."""
|
|
|
|
assert isinstance(dev, SmartDevice)
|
|
|
|
assert "brightness" in dev._components
|
|
|
|
|
|
|
|
# Test getting the value
|
|
|
|
feature = dev.features["brightness"]
|
|
|
|
assert feature.minimum_value == 1
|
|
|
|
assert feature.maximum_value == 100
|
|
|
|
|
|
|
|
await dev.set_brightness(10)
|
|
|
|
await dev.update()
|
|
|
|
assert dev.brightness == 10
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
await dev.set_brightness(feature.minimum_value - 10)
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
await dev.set_brightness(feature.maximum_value + 10)
|