diff --git a/kasa/smart/smartdevice.py b/kasa/smart/smartdevice.py index 6be2392c..e6402131 100644 --- a/kasa/smart/smartdevice.py +++ b/kasa/smart/smartdevice.py @@ -109,9 +109,23 @@ class SmartDevice(Device): can't be created to avoid spamming the logs on every update. """ changed = False + if ( + child_component_list := child_device_components_resp.get( + "child_component_list" + ) + ) is None: + # Some hubs intermittently return the component list without the + # list itself, e.g. an H200 without children. Keep the known children + # instead of deleting them, the next update will reconcile. + _LOGGER.debug( + "No child component list for device %s, response: %s", + self.host, + child_device_components_resp, + ) + return changed + smart_children_components = { - child["device_id"]: child - for child in child_device_components_resp["child_component_list"] + child["device_id"]: child for child in child_component_list } children = self._children child_ids: set[str] = set() diff --git a/tests/smart/test_smartdevice.py b/tests/smart/test_smartdevice.py index aea73381..f497b5d1 100644 --- a/tests/smart/test_smartdevice.py +++ b/tests/smart/test_smartdevice.py @@ -876,6 +876,19 @@ async def test_dynamic_devices(dev: Device, caplog: pytest.LogCaptureFixture) -> expected_new_length -= 1 assert len(dev.children) == expected_new_length + # Test component list response without the list keeps the known children + + saved_child_device_components = mock_child_device_components + mock_child_device_components = {} + + with patch.object( + transport, "get_child_device_queries", side_effect=mock_get_child_device_queries + ): + await dev.update() + + assert len(dev.children) == expected_new_length + mock_child_device_components = saved_child_device_components + # Test no child devices mock_child_device_info["child_device_list"] = [] @@ -890,6 +903,20 @@ async def test_dynamic_devices(dev: Device, caplog: pytest.LogCaptureFixture) -> assert len(dev.children) == 0 + # Test component list response without the list on a hub without children, + # as returned intermittently by some hubs (e.g. H200 firmware 1.7.7) + + saved_child_device_components = mock_child_device_components + mock_child_device_components = {} + + with patch.object( + transport, "get_child_device_queries", side_effect=mock_get_child_device_queries + ): + await dev.update() + + assert len(dev.children) == 0 + mock_child_device_components = saved_child_device_components + # Logging tests are only for smartcam hubs as smart hubs do not test categories if not isinstance(dev, SmartCamDevice): return