Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Fix H500 hub child list handling in SMART flows.#1695

Open
jimboca wants to merge 3 commits into
python-kasa:masterpython-kasa/python-kasa:masterfrom
jimboca:masterjimboca/python-kasa:masterCopy head branch name to clipboard
Open

Fix H500 hub child list handling in SMART flows.#1695
jimboca wants to merge 3 commits into
python-kasa:masterpython-kasa/python-kasa:masterfrom
jimboca:masterjimboca/python-kasa:masterCopy head branch name to clipboard

Conversation

@jimboca
Copy link
Copy Markdown
Contributor

@jimboca jimboca commented May 9, 2026

Handle paginated SMART/SMARTCAM child responses that omit list fields to avoid StopIteration and KeyError crashes on Tapo H500, and add regression coverage for the no-list pagination shape.

Summary

  • Fix SmartProtocol._handle_response_lists to safely handle paginated responses that include start_index and sum but do not include any list-typed fields.
  • Prevent StopIteration (from next(iter(...))) which surfaces as RuntimeError: coroutine raised StopIteration in async flows.
  • Add regression test test_handle_response_lists_no_list_fields in tests/protocols/test_smartprotocol.py.

Why

Some devices (observed with Tapo H500) can return a paginated response shape where no list key is present. Current code assumes at least one list field exists and raises at runtime. This causes repeated update failures during negotiation/discovery.

Changes

  • In kasa/protocols/smartprotocol.py:
    • Replace implicit next(iter([...])) assumption with:
      • collect list_keys
      • if empty: log a warning and return without attempting additional pages
      • otherwise proceed with the first list key
  • In tests/protocols/test_smartprotocol.py:
    • Add a regression test covering start_index/sum + non-list payload shape.

Test Plan

  • Run targeted regression test:
    • python -m pytest -o addopts='' tests/protocols/test_smartprotocol.py::test_handle_response_lists_no_list_fields -q --tb=short
  • Verify existing list pagination behavior remains unchanged via existing tests around list handling.
  • Local functional validation against environment reproducing H500 negotiation path (no StopIteration / runtime crash in this path).

Notes

  • First change resulted in a traceback still showing up:
    2026-05-06 07:21:49.180 Thread-10 (run_forever) udi_interface ERROR Controller:update_dev: Failed to update 'child_component_list': <DeviceType.Hub at 192.168.1.150 - Tapo_H500_06D8 (H50
    0)>
    Traceback (most recent call last):
    File "/usr/home/admin/dev/pg3/plugins/udi-poly-kasa/nodes/Controller.py", line 207, in update_dev
    await dev.update()
    File "/usr/home/admin/dev/pg3/plugins/udi-poly-kasa/kasa/smart/smartdevice.py", line 279, in update
    children_changed = await self._update_children_info()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/home/admin/dev/pg3/plugins/udi-poly-kasa/kasa/smartcam/smartcamdevice.py", line 114, in _update_children_info
    changed = await self._create_delete_children(
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/home/admin/dev/pg3/plugins/udi-poly-kasa/kasa/smart/smartdevice.py", line 114, in _create_delete_children
    for child in child_device_components_resp["child_component_list"]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
    KeyError: 'child_component_list'

  • After plugin restart, logs confirmed:

    • RuntimeError: coroutine raised StopIteration no longer appears.
    • New warning path is active repeatedly:
      • smartprotocol:_handle_response_lists ... no list fields in result ... skipping additional list pages
    • Remaining H500 failure is KeyError: 'child_component_list' in
      kasa/smart/smartdevice.py::_create_delete_children.
  • Follow-up fix applied in kasa/smart/smartdevice.py:

    • Guard child_device_components_resp.get("child_component_list") and
      child_device_resp.get("child_device_list").
    • If either is missing or not a list, log a warning and treat as empty list.
    • Build smart_children_components only from dict entries containing device_id.
    • This prevents crashes when H500 child component payload is incomplete while
      preserving behavior for normal payloads.
  • This change is intentionally narrow and defensive: it only alters behavior when no list-typed fields exist in a response that otherwise looks paginated.

  • Existing behavior for normal paginated list responses is unchanged.

Handle paginated SMART/SMARTCAM child responses that omit list fields to avoid StopIteration and KeyError crashes on Tapo H500, and add regression coverage for the no-list pagination shape.

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 82.85714% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.18%. Comparing base (76d9f68) to head (7a5b2b7).

Files with missing lines Patch % Lines
kasa/smart/smartdevice.py 75.00% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1695      +/-   ##
==========================================
- Coverage   93.22%   93.18%   -0.05%     
==========================================
  Files         157      157              
  Lines        9815     9839      +24     
  Branches     1003     1010       +7     
==========================================
+ Hits         9150     9168      +18     
- Misses        472      475       +3     
- Partials      193      196       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

jimboca and others added 2 commits May 9, 2026 10:07
Exercise the H500 defensive child-list fallbacks directly so Codecov sees the new no-list paths as covered.

Co-authored-by: Cursor <cursoragent@cursor.com>
…els.

Treat non-dict get_device_info/component_nego payloads as transient device errors and make Device.__repr__ fall back to a safe host-only form, so H500 and camera responses with SmartErrorCode do not blow up logging or model access. Keep the repr fallback explicitly string-typed so mypy accepts the exception path as well.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.