From 4eed945e002623a82e35005085d6e1e4ad79c68f Mon Sep 17 00:00:00 2001 From: "Steven B." <51370195+sdb9696@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:14:45 +0000 Subject: [PATCH] Do not error when accessing smart device_type before update (#1319) --- kasa/smart/smartdevice.py | 9 +++++---- tests/discovery_fixtures.py | 2 ++ tests/smart/test_smartdevice.py | 30 +++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/kasa/smart/smartdevice.py b/kasa/smart/smartdevice.py index adb4829d..176efb71 100644 --- a/kasa/smart/smartdevice.py +++ b/kasa/smart/smartdevice.py @@ -765,10 +765,11 @@ class SmartDevice(Device): if self._device_type is not DeviceType.Unknown: return self._device_type - # Fallback to device_type (from disco info) - type_str = self._info.get("type", self._info.get("device_type")) - - if not type_str: # no update or discovery info + if ( + not (type_str := self._info.get("type", self._info.get("device_type"))) + or not self._components + ): + # no update or discovery info return self._device_type self._device_type = self._get_device_type_from_components( diff --git a/tests/discovery_fixtures.py b/tests/discovery_fixtures.py index c65d47bd..93921536 100644 --- a/tests/discovery_fixtures.py +++ b/tests/discovery_fixtures.py @@ -130,6 +130,8 @@ new_discovery = parametrize_discovery( "new discovery", data_root_filter="discovery_result" ) +smart_discovery = parametrize_discovery("smart discovery", protocol_filter={"SMART"}) + @pytest.fixture( params=filter_fixtures("discoverable", protocol_filter={"SMART", "IOT"}), diff --git a/tests/smart/test_smartdevice.py b/tests/smart/test_smartdevice.py index c53193a3..81707a11 100644 --- a/tests/smart/test_smartdevice.py +++ b/tests/smart/test_smartdevice.py @@ -2,6 +2,7 @@ from __future__ import annotations +import copy import logging import time from typing import Any, cast @@ -11,16 +12,18 @@ import pytest from freezegun.api import FrozenDateTimeFactory from pytest_mock import MockerFixture -from kasa import Device, KasaException, Module +from kasa import Device, DeviceType, KasaException, Module from kasa.exceptions import DeviceError, SmartErrorCode from kasa.protocols.smartprotocol import _ChildProtocolWrapper from kasa.smart import SmartDevice from kasa.smart.modules.energy import Energy from kasa.smart.smartmodule import SmartModule from tests.conftest import ( + DISCOVERY_MOCK_IP, device_smart, get_device_for_fixture_protocol, get_parent_and_child_modules, + smart_discovery, ) from tests.device_fixtures import variable_temp_smart @@ -51,6 +54,31 @@ async def test_update_no_device_info(dev: SmartDevice, mocker: MockerFixture): await dev.update() +@smart_discovery +async def test_device_type_no_update(discovery_mock, caplog: pytest.LogCaptureFixture): + """Test device type and repr when device not updated.""" + dev = SmartDevice(DISCOVERY_MOCK_IP) + assert dev.device_type is DeviceType.Unknown + assert repr(dev) == f"" + + discovery_result = copy.deepcopy(discovery_mock.discovery_data["result"]) + dev.update_from_discover_info(discovery_result) + assert dev.device_type is DeviceType.Unknown + assert ( + repr(dev) + == f"" + ) + discovery_result["device_type"] = "SMART.FOOBAR" + dev.update_from_discover_info(discovery_result) + dev._components = {"dummy": 1} + assert dev.device_type is DeviceType.Plug + assert ( + repr(dev) + == f"" + ) + assert "Unknown device type, falling back to plug" in caplog.text + + @device_smart async def test_initial_update(dev: SmartDevice, mocker: MockerFixture): """Test the initial update cycle."""