FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Support new_klap IoT devices by ZeliardM · Pull Request #1580 · python-kasa/python-kasa · GitHub

Repository navigation

Support new_klap IoT devices - #1580

Open
ZeliardM wants to merge 37 commits into
python-kasa:masterfrom
ZeliardM:feature/new-klap
Open

ZeliardM wants to merge 37 commits into
python-kasa:masterfrom
ZeliardM:feature/new-klap

Conversation

ZeliardM commented Sep 29, 2025 •
edited
Loading

Copy link
Copy Markdown
Collaborator

Implementing KlapTranportV2 to handle IoT devices with newer firmware and a new_klap flag and all associated tests hopefully.

With this PR, I will get the user to pull the specific branch for testing on their end, and I will test on my end to make sure that the old stuff still functions as well.

codecov Bot commented Sep 29, 2025 •
edited
Loading

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.40%. Comparing base (88e1c27) to head (71a4147).
⚠️ Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
kasa/cli/discover.py 66.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1580      +/-   ##
==========================================
+ Coverage   93.21%   93.40%   +0.18%     
==========================================
  Files         157      157              
  Lines        9819     9870      +51     
  Branches     1005     1013       +8     
==========================================
+ Hits         9153     9219      +66     
+ Misses        472      462      -10     
+ Partials      194      189       -5     

☔ View full report in Codecov by Harness.
📢 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.

Copy link
Copy Markdown
Collaborator Author

@rytilahti I haven't seen this before:
GitHub Advanced Security / CodeQL
failed 44 minutes ago in 3s
1 new alert including 1 high severity security vulnerability
New alerts in code changed by this pull request
Security Alerts:

1 high
See annotations below for details.

View all branch alerts.

Annotations
Check failure on line 85 in kasa/transports/klaptransport.py

Code scanning
/ CodeQL

Use of a broken or weak cryptographic hashing algorithm on sensitive data
High

is used in a hashing algorithm (SHA1) that is insecure.
is used in a hashing algorithm (SHA1) that is insecure.
is used in a hashing algorithm (SHA1) that is insecure for password hashing, since it is not a computationally expensive hash function.
is used in a hashing algorithm (SHA1) that is insecure for password hashing, since it is not a computationally expensive hash function.

Is this because I modified this file and now it's looking at it? You had the code they flag already in your code, but I'm not sure why it's being flagged as a problem now.

Copy link
Copy Markdown
Collaborator Author

Ok, had my user test and the discovery and CLI tests were successful, but we're getting 400 error responses at first with handshake1 one or two times and then it works. Not sure what that's about yet, will keep poking around.

ZeliardM commented Oct 1, 2025

Copy link
Copy Markdown
Collaborator Author

@rytilahti Ok, here's where it gets weird. I found that it actually isn't a new KLAP, it still uses XOR. The issue isn't the actual transport, it's how it's selecting the transport. I will modify things so that in the device factory, it's still forcing XOR if the device has new_klap. It's a much easier fix and has been working consistently on my end.

Copilot AI review requested due to automatic review settings October 2, 2025 15:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull Request Overview

This PR implements KlapTransportV3 for the new_klap protocol by adding support for a new "new_klap" parameter throughout the discovery and device configuration system. The implementation allows devices that support this new KLAP variant to be properly detected and configured to use XorTransport instead of the standard KlapTransport.

Key changes:

  • Added new_klap parameter to device configuration and discovery classes
  • Implemented special handling in discovery protocol to finalize devices with new_klap capability
  • Updated device factory to route new_klap devices to XorTransport instead of KlapTransport

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
kasa/deviceconfig.py Added new_klap field to DeviceConnectionParameters with custom serialization
kasa/discover.py Added new_klap support to discovery protocol with special finalization logic
kasa/device_factory.py Added routing logic to use XorTransport for new_klap devices
tests/test_discovery.py Added comprehensive test for new_klap discovery finalization behavior
tests/test_deviceconfig.py Added test configuration and serialization test for new_klap
tests/test_device_factory.py Added test case for new_klap protocol selection
devtools/dump_devinfo.py Updated device info capture to include new_klap parameter
Comments suppressed due to low confidence (1)

tests/test_cli.py:1

  • [nitpick] The test logic has changed from using explicit assert_not_called() and assert_called() to comparing call counts. This approach is less clear about the expected behavior and makes the test harder to understand. Consider using more descriptive assertions or adding comments explaining why call counts are being compared.
import json

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

ZeliardM commented Oct 2, 2025

Copy link
Copy Markdown
Collaborator Author

@rytilahti Ok, so you can understand what I did here. I managed to make it where during discovery, it adds a new_klap portion to the management schema if it exists. Then it checks if that exists and if so, it runs the XOR transport on the IOT protocol. Otherwise, it was trying to actually use the KLAP transport which even though it says KLAP it's XOR. This allows the device to initially be discovered, but the problem comes up where the KLAP discovery results don't allow for the segmented device types for IOT. So, I update the device one time, pull the sysinfo, then mutate that back to what a XOR discovery result would be and re-initialize the device from the sysinfo. This seems to be working quite well and reliably for me and the one user who has been testing this with me, @allenrabinovich. Old IOT and new SMART devices are working correctly for me as well.

rytilahti left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

A couple of quick comments, also, please do not forget to change the title of this PR when this is ready for a review :-)

Comment thread kasa/device_factory.py Outdated
Comment thread kasa/deviceconfig.py Outdated
Comment thread kasa/discover.py Outdated
Comment thread kasa/cli/hub.py
ZeliardM changed the title Implement KlapTransportV3 for new_klap Implement KlapTransportV2 for new_klap Oct 7, 2025

ZeliardM commented Oct 7, 2025

Copy link
Copy Markdown
Collaborator Author

@rytilahti Ok, I think I got everything figured out. It is actually KlapTransportV2. I have modified everything to handle this and updated all of the test coverage as well. Everything looks good on my end as well.

rytilahti left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I cannot test this mysef, but it looks good on surface. I would suggest tidying it up a bit to keep the code more maintainable if and when new authentication methods pop up, but your call!

Comment thread tests/test_cli.py Outdated
Comment thread tests/test_discovery.py Outdated
Comment thread kasa/device_factory.py Outdated
Comment thread kasa/discover.py Outdated
Comment thread kasa/discover.py Outdated
rytilahti added the enhancement New feature or request label Oct 11, 2025
rytilahti added this to the 0.11 milestone Oct 11, 2025

Copy link
Copy Markdown

I tested this locally and it's working well so far.
Model: HS300 (power strip)
Firmware: 1.1.2 Build 241220 Rel.171333 (I think this is the latest as of 2025-10-30)

Thanks!

Copy link
Copy Markdown

Any idea when this might make it to production? I had been using the patch in this post after each HomeAssistant update but after the latest 2026.2.3 the patch doesn't work anymore.

genzj commented Apr 27, 2026

Copy link
Copy Markdown

test log

I tested the PR and it's working with HS103 firmware 1.1.3.

Reminder: after upgrading the plug to firmware 1.1.3, even correct credentials can trigger an authentication error. The fix was to unregister the plug from the app and reinitialize it.

(.venv) user@host:~/kasa$ kasa -v -d --host REDACTED_IP sysinfo
Discovering device REDACTED_IP for 10 seconds
DEBUG:kasa.discover:[DISCOVERY] REDACTED_IP >> {'system': {'get_sysinfo': {}}}
DEBUG:kasa.discover:Waiting a total of 10 seconds for responses...
DEBUG:kasa.device_factory:Finding protocol for REDACTED_IP
DEBUG:kasa.device_factory:Finding protocol for DeviceFamily.IotSmartPlugSwitch
DEBUG:kasa.device_factory:Finding transport for IOT.KLAP.NEW_KLAP
DEBUG:kasa.transports.klaptransport:Created KLAP transport for REDACTED_IP
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP >> {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Starting handshake with REDACTED_IP
DEBUG:kasa.httpclient:Posting to http://REDACTED_IP/app/handshake1
DEBUG:kasa.transports.klaptransport:Handshake1 posted at 2026-04-26 20:39:38.854634. Host is REDACTED_IP, Response status is 200, Request was REDACTED_REQUEST_ID
DEBUG:kasa.transports.klaptransport:Handshake1 success at 2026-04-26 20:39:38.854874. Host is REDACTED_IP, Server remote_seed is: REDACTED_REMOTE_SEED, server hash is: REDACTED_SERVER_HASH
DEBUG:kasa.transports.klaptransport:handshake1 hashes match with expected credentials
DEBUG:kasa.httpclient:Posting to http://REDACTED_IP/app/handshake2
DEBUG:kasa.transports.klaptransport:Handshake2 posted 2026-04-26 20:39:38.860822. Host is REDACTED_IP, Response status is 200, Request was REDACTED_REQUEST_ID_2
DEBUG:kasa.transports.klaptransport:Handshake with REDACTED_IP complete
DEBUG:kasa.httpclient:Posting to http://REDACTED_IP/app/request
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query posted Host is REDACTED_IP, Sequence is -1639787443, Response status is 200, Request was {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query response received
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP << {'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_DEVICE_ID',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_HW_ID',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': 'REDACTED_MAC',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_OEM_ID',
                            'on_time': 0,
                            'relay_state': 0,
                            'rssi': -26,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}}}
DEBUG:kasa.device:Initializing REDACTED_IP of type <class 'kasa.iot.iotplug.IotPlug'>
DEBUG:kasa.discover:[DISCOVERY] REDACTED_IP << {'error_code': 0,
 'result': {'device_id': 'REDACTED_DEVICE_ID',
            'device_model': 'HS103(US)',
            'device_type': 'IOT.SMARTPLUGSWITCH',
            'factory_default': False,
            'hw_ver': '5.0',
            'ip': 'REDACTED_IP',
            'mac': 'REDACTED_MAC',
            'mgt_encrypt_schm': {'ANS': False,
                                 'encrypt_type': 'KLAP',
                                 'http_port': 80,
                                 'is_support_https': False,
                                 'lv': 2,
                                 'new_klap': 1},
            'obd_src': 'tplink',
            'owner': 'REDACTED_OWNER',
            'protocol_version': 1}}
DEBUG:kasa.iot.iotdevice:Performing the initial update to obtain sysinfo
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP >> {"system":{"get_sysinfo":{}}}
DEBUG:kasa.httpclient:Posting to http://REDACTED_IP/app/request
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query posted Host is REDACTED_IP, Sequence is -1639787442, Response status is 200, Request was {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query response received
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP << {'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_DEVICE_ID',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_HW_ID',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': 'REDACTED_MAC',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_OEM_ID',
                            'on_time': 0,
                            'relay_state': 0,
                            'rssi': -26,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}}}
DEBUG:kasa.iot.iotdevice:Adding module <Module HomeKit (smartlife.iot.homekit) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Schedule (schedule) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Usage (schedule) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Antitheft (anti_theft) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Time (time) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Cloud (cnCloud) for REDACTED_IP>
DEBUG:kasa.iot.iotdevice:Adding module <Module Led (system) for REDACTED_IP>
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: smartlife.iot.homekit
DEBUG:kasa.iot.iotdevice:Adding query for <Module HomeKit (smartlife.iot.homekit) for REDACTED_IP>: {'smartlife.iot.homekit': {'setup_info_get': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: schedule
DEBUG:kasa.iot.iotdevice:Adding query for <Module Schedule (schedule) for REDACTED_IP>: {'schedule': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: schedule
DEBUG:kasa.iot.iotdevice:Adding query for <Module Usage (schedule) for REDACTED_IP>: {'schedule': {'get_realtime': {}, 'get_daystat': {'year': 2026, 'month': 4}, 'get_monthstat': {'year': 2026}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: anti_theft
DEBUG:kasa.iot.iotdevice:Adding query for <Module Antitheft (anti_theft) for REDACTED_IP>: {'anti_theft': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: time
DEBUG:kasa.iot.iotdevice:Adding query for <Module Time (time) for REDACTED_IP>: {'time': {'get_time': {}, 'get_timezone': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: cnCloud
DEBUG:kasa.iot.iotdevice:Adding query for <Module Cloud (cnCloud) for REDACTED_IP>: {'cnCloud': {'get_info': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Led (system) for REDACTED_IP>: {}
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP >> {"system":{"get_sysinfo":{}},"smartlife.iot.homekit":{"setup_info_get":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":4},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.httpclient:Posting to http://REDACTED_IP/app/request
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query posted Host is REDACTED_IP, Sequence is -1639787441, Response status is 200, Request was {"system":{"get_sysinfo":{}},"smartlife.iot.homekit":{"setup_info_get":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":4},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.transports.klaptransport:Device REDACTED_IP query response received
DEBUG:kasa.protocols.iotprotocol:REDACTED_IP << {'anti_theft': {'get_next_action': {'err_code': -2,
                                    'err_msg': 'member not support'},
                'get_rules': {'enable': 0,
                              'err_code': 0,
                              'rule_list': [],
                              'version': 2}},
 'cnCloud': {'get_info': {'binded': 1,
                          'cld_connection': 1,
                          'err_code': 0,
                          'fwDlPage': '',
                          'fwNotifyType': -1,
                          'illegalType': 0,
                          'server': 'n-devs.tplinkcloud.com',
                          'stopConnect': 0,
                          'tcspInfo': '',
                          'tcspStatus': 1,
                          'username': 'REDACTED_USERNAME'}},
 'schedule': {'get_daystat': {'day_list': [{'day': 26,
                                            'month': 4,
                                            'time': 68,
                                            'year': 2026}],
                              'err_code': 0},
              'get_monthstat': {'err_code': 0,
                                'month_list': [{'month': 4,
                                                'time': 68,
                                                'year': 2026}]},
              'get_next_action': {'err_code': 0, 'type': -1},
              'get_realtime': {'err_code': -2, 'err_msg': 'member not support'},
              'get_rules': {'enable': 1,
                            'err_code': 0,
                            'rule_list': [],
                            'version': 2}},
 'smartlife.iot.homekit': {'err_code': -1, 'err_msg': 'module not support'},
 'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_DEVICE_ID',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_HW_ID',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': 'REDACTED_MAC',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_OEM_ID',
                            'on_time': 0,
                            'relay_state': 0,
                            'rssi': -26,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}},
 'time': {'get_time': {'err_code': 0,
                       'hour': 20,
                       'mday': 26,
                       'min': 39,
                       'month': 4,
                       'sec': 38,
                       'year': 2026},
          'get_timezone': {'err_code': 0, 'index': 6}}}
== System info ==
{'active_mode': 'none',
 'alias': '#MASKED_NAME#',
 'dev_name': 'Smart Wi-Fi Plug Mini',
 'deviceId': 'REDACTED_DEVICE_ID',
 'err_code': 0,
 'feature': 'TIM',
 'hwId': 'REDACTED_HW_ID',
 'hw_ver': '5.0',
 'icon_hash': '',
 'latitude_i': REDACTED_LATITUDE,
 'led_off': 0,
 'lnk_on': 0,
 'longitude_i': REDACTED_LONGITUDE,
 'mac': 'REDACTED_MAC',
 'mic_type': 'IOT.SMARTPLUGSWITCH',
 'model': 'HS103(US)',
 'next_action': {'type': -1},
 'obd_src': 'tplink',
 'oemId': 'REDACTED_OEM_ID',
 'on_time': 0,
 'relay_state': 0,
 'rssi': -26,
 'status': 'new',
 'sw_ver': '1.1.3 Build 250908 Rel.112508',
 'updating': 0}

Copy link
Copy Markdown
Collaborator Author

@genzj Thanks for the test and information. There are still some transient issues with Discover and how devices respond that can cause some issues but overall this fix works. I have several other PRs that I'm trying to get all worked on and merged before another release but I'm not sure when that will happen.

Copy link
Copy Markdown

any update on the status of this PR? my two HS300s suddenly stopped working with home-assistant today and I think this is why...

ZeliardM changed the title Implement KlapTransportV2 for new_klap Support new_klap IoT devices Jul 8, 2026

ZeliardM commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up pass pushed in 71a4147.

Covered in this pass:

  • Resolved the remaining device factory review thread by keeping direct-connect IoT class selection based on the queried get_sysinfo response instead of the discovery response, with regression coverage in test_connect_iot_family_resolves_subclass_from_sysinfo.
  • Tightened tests/test_discovery.py discovery payload typing with TypedDict/structured annotations so the new KLAP fixture data no longer falls back to Any.
  • Matched the annotation direction from tests: add type annotations to top-level test files #1688 for the overlapping test files; tests: add type annotations to transport tests #1684 remains transport-test-only and does not overlap this PR's changed paths.
  • Fixed discover config output for new_klap=True devices so it prints a round-trippable --encrypt-type KLAPV2 value instead of the internal KLAP value.
  • Updated the PR title to Support new_klap IoT devices.

Local validation:

  • uv run pytest -q passed: 22547 passed, 852 skipped.
  • Targeted ruff check, ruff format --check, mypy, and git diff --check passed for the touched files.

After the push, GitHub lint and CodeQL were already green; the matrix CI/readthedocs jobs were still running when I checked.

Copy link
Copy Markdown

Independent real-device validation of the current head (71a4147984aedc7cfa9ce4613204ecf28b15dac7) against an HS300 that fails with python-kasa 0.10.2 because discovery advertises IOT + KLAP with login version 2 while that release selects the v1 transport.

Device details:

  • Model: HS300 (US)
  • Hardware: 2.0
  • Firmware: 1.1.2 Build 241220 Rel.171333
  • Discovery: IOT.SMARTPLUGSWITCH, KLAP, lv=2, new_klap=1

Results with this PR:

  • Discover.discover_single() selected KlapTransportV2.
  • The device was correctly instantiated as IotStrip, not IotPlug.
  • All six child outlets were present and their state was readable.
  • After serializing and recreating DeviceConfig, Device.connect(config=...) again selected KlapTransportV2, returned IotStrip, and exposed all six children.

The same physical device currently requires an application-level workaround that forces KlapTransportV2 when using 0.10.2. This PR makes that workaround unnecessary. Credentials, network addresses, MAC addresses, device IDs, and outlet names were excluded from this report.

Independent HS300 validation: works as expected.

thehcma commented Aug 21, 2026

Copy link
Copy Markdown

hi @ZeliardM - do you know if/when this PR will be merged and a new release created?

Copy link
Copy Markdown

This PR also helped me bypass an authentication error.

== TP-LINK_Smart Plug_FA58 - HS103 ==
Hardware:     5.0 (US)
Firmware:     1.1.3 Build 250908 Rel.112508

Initially, I connected to SSID of plug (which had previously been connected to cloud), then ran:

uv run kasa --debug --username "username@example.com" --password "some&passw123rd!" --host 192.168.0.1 time sync
debug log of current master
Discovering device 192.168.0.1 for 10 seconds
DEBUG:kasa.discover:[DISCOVERY] 192.168.0.1 >> {'system': {'get_sysinfo': {}}}
DEBUG:kasa.discover:Waiting a total of 10 seconds for responses...
DEBUG:kasa.device_factory:Using IotPlug for IOT.SMARTPLUGSWITCH
DEBUG:kasa.device_factory:Finding protocol for 192.168.0.1
DEBUG:kasa.device_factory:Finding protocol for DeviceFamily.IotSmartPlugSwitch
DEBUG:kasa.device_factory:Finding transport for IOT.KLAP
DEBUG:kasa.transports.klaptransport:Created KLAP transport for 192.168.0.1
DEBUG:kasa.discover:[DISCOVERY] 192.168.0.1 << {'error_code': 0,
 'result': {'device_id': 'REDACTED_bb86dbf1b674c79addd0c8b',
            'device_model': 'HS103(US)',
            'device_type': 'IOT.SMARTPLUGSWITCH',
            'factory_default': True,
            'hw_ver': '5.0',
            'ip': '192.168.0.1',
            'mac': '00-5F-67-00-00-00',
            'mgt_encrypt_schm': {'ANS': False,
                                 'encrypt_type': 'KLAP',
                                 'http_port': 80,
                                 'is_support_https': False,
                                 'lv': 2,
                                 'new_klap': 1},
            'obd_src': 'tplink',
            'owner': '',
            'protocol_version': 1}}
DEBUG:kasa.device:Initializing 192.168.0.1 of type <class 'kasa.iot.iotplug.IotPlug'>
DEBUG:kasa.iot.iotdevice:Performing the initial update to obtain sysinfo
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Starting handshake with 192.168.0.1
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/handshake1
DEBUG:kasa.transports.klaptransport:Handshake1 posted at 2026-08-29 11:21:29.686484. Host is 192.168.0.1, Response status is 200, Request was 01eb02fd6c3347ed5495df770f9e90cb
DEBUG:kasa.transports.klaptransport:Handshake1 success at 2026-08-29 11:21:29.686616. Host is 192.168.0.1, Server remote_seed is: 3ec60589e231868b15df048a7b24058b, server hash is: 5234c1ddfd45ae354d03d9f44732f4fde8ba02e8f74cb537b081fb08ca4532a7
DEBUG:kasa.transports.klaptransport:Device response did not match our challenge on ip 192.168.0.1, check that your e-mail and password (both case-sensitive) are correct.
DEBUG:kasa.protocols.iotprotocol:Unable to authenticate with 192.168.0.1, not retrying
Raised error: Device response did not match our challenge on ip 192.168.0.1, check that your e-mail and password (both case-sensitive) are correct.
git fetch origin pull/1580/head:pr-1580-ZeliardM
git checkout pr-1580-ZeliardM
debug log after patch
Discovering device 192.168.0.1 for 10 seconds
DEBUG:kasa.discover:[DISCOVERY] 192.168.0.1 >> {'system': {'get_sysinfo': {}}}
DEBUG:kasa.discover:Waiting a total of 10 seconds for responses...
DEBUG:kasa.device_factory:Finding protocol for 192.168.0.1
DEBUG:kasa.device_factory:Finding protocol for DeviceFamily.IotSmartPlugSwitch
DEBUG:kasa.device_factory:Finding transport for IOT.KLAP.NEW_KLAP
DEBUG:kasa.transports.klaptransport:Created KLAP transport for 192.168.0.1
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Starting handshake with 192.168.0.1
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/handshake1
DEBUG:kasa.transports.klaptransport:Handshake1 posted at 2026-08-29 11:30:29.847252. Host is 192.168.0.1, Response status is 200, Request was 49f7e64c63a0111ee34dcb3d5112addd
DEBUG:kasa.transports.klaptransport:Handshake1 success at 2026-08-29 11:30:29.847471. Host is 192.168.0.1, Server remote_seed is: c04149b402724bb431134bb5d00a4ab5, server hash is: 6235debdd4ffae301ef0e836087f4ae4cd612285ec74c70b72a31fe78491eef8
DEBUG:kasa.transports.klaptransport:Device response did not match our expected hash on ip 192.168.0.1,but an authentication with KASA default credentials worked
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/handshake2
DEBUG:kasa.transports.klaptransport:Handshake2 posted 2026-08-29 11:30:29.852758. Host is 192.168.0.1, Response status is 200, Request was 4b15dd64f589940b3b38315eee0bc77633dac8bd47b1526e7392665ec0ff40ff
DEBUG:kasa.transports.klaptransport:Handshake with 192.168.0.1 complete
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/request
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query posted Host is 192.168.0.1, Sequence is -534551261, Response status is 200, Request was {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query response received
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 << {'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_2DAB5A958F38BCCFF8352E51E114A5E',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_1DD892EA69AB42199F59E41',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': '00:5F:67:00:00:00',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_6FA93568D818524FE170CEC',
                            'on_time': 2393,
                            'relay_state': 1,
                            'rssi': -41,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}}}
DEBUG:kasa.device:Initializing 192.168.0.1 of type <class 'kasa.iot.iotplug.IotPlug'>
DEBUG:kasa.discover:[DISCOVERY] 192.168.0.1 << {'error_code': 0,
 'result': {'device_id': 'REDACTED_bb86dbf1b674c79addd0c8b',
            'device_model': 'HS103(US)',
            'device_type': 'IOT.SMARTPLUGSWITCH',
            'factory_default': True,
            'hw_ver': '5.0',
            'ip': '192.168.0.1',
            'mac': '00-5F-67-00-00-00',
            'mgt_encrypt_schm': {'ANS': False,
                                 'encrypt_type': 'KLAP',
                                 'http_port': 80,
                                 'is_support_https': False,
                                 'lv': 2,
                                 'new_klap': 1},
            'obd_src': 'tplink',
            'owner': '',
            'protocol_version': 1}}
DEBUG:kasa.iot.iotdevice:Performing the initial update to obtain sysinfo
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"system":{"get_sysinfo":{}}}
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/request
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query posted Host is 192.168.0.1, Sequence is -534551260, Response status is 200, Request was {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query response received
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 << {'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_2DAB5A958F38BCCFF8352E51E114A5E',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_1DD892EA69AB42199F59E41',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': '00:5F:67:00:00:00',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_6FA93568D818524FE170CEC',
                            'on_time': 2393,
                            'relay_state': 1,
                            'rssi': -41,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}}}
DEBUG:kasa.iot.iotdevice:Adding module <Module HomeKit (smartlife.iot.homekit) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Schedule (schedule) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Usage (schedule) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Antitheft (anti_theft) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Time (time) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Cloud (cnCloud) for 192.168.0.1>
DEBUG:kasa.iot.iotdevice:Adding module <Module Led (system) for 192.168.0.1>
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: smartlife.iot.homekit
DEBUG:kasa.iot.iotdevice:Adding query for <Module HomeKit (smartlife.iot.homekit) for 192.168.0.1>: {'smartlife.iot.homekit': {'setup_info_get': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: schedule
DEBUG:kasa.iot.iotdevice:Adding query for <Module Schedule (schedule) for 192.168.0.1>: {'schedule': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: schedule
DEBUG:kasa.iot.iotdevice:Adding query for <Module Usage (schedule) for 192.168.0.1>: {'schedule': {'get_realtime': {}, 'get_daystat': {'year': 2026, 'month': 8}, 'get_monthstat': {'year': 2026}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: anti_theft
DEBUG:kasa.iot.iotdevice:Adding query for <Module Antitheft (anti_theft) for 192.168.0.1>: {'anti_theft': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: time
DEBUG:kasa.iot.iotdevice:Adding query for <Module Time (time) for 192.168.0.1>: {'time': {'get_time': {}, 'get_timezone': {}}}
DEBUG:kasa.iot.iotmodule:Initial update, so consider supported: cnCloud
DEBUG:kasa.iot.iotdevice:Adding query for <Module Cloud (cnCloud) for 192.168.0.1>: {'cnCloud': {'get_info': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Led (system) for 192.168.0.1>: {}
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"system":{"get_sysinfo":{}},"smartlife.iot.homekit":{"setup_info_get":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":8},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/request
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query posted Host is 192.168.0.1, Sequence is -534551259, Response status is 200, Request was {"system":{"get_sysinfo":{}},"smartlife.iot.homekit":{"setup_info_get":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":8},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query response received
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 << {'anti_theft': {'get_next_action': {'err_code': -2,
                                    'err_msg': 'member not support'},
                'get_rules': {'err_code': -11, 'err_msg': 'time not sync'}},
 'cnCloud': {'get_info': {'binded': 0,
                          'cld_connection': 0,
                          'err_code': 0,
                          'fwDlPage': '',
                          'fwNotifyType': -1,
                          'illegalType': 0,
                          'server': 'n-devs.tplinkcloud.com',
                          'stopConnect': 0,
                          'tcspInfo': '',
                          'tcspStatus': 0,
                          'username': ''}},
 'schedule': {'get_daystat': {'day_list': [], 'err_code': 0},
              'get_monthstat': {'err_code': 0, 'month_list': []},
              'get_next_action': {'err_code': 0, 'type': -1},
              'get_realtime': {'err_code': -2, 'err_msg': 'member not support'},
              'get_rules': {'err_code': -11, 'err_msg': 'time not sync'}},
 'smartlife.iot.homekit': {'err_code': -1, 'err_msg': 'module not support'},
 'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_2DAB5A958F38BCCFF8352E51E114A5E',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_1DD892EA69AB42199F59E41',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': '00:5F:67:00:00:00',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_6FA93568D818524FE170CEC',
                            'on_time': 2394,
                            'relay_state': 1,
                            'rssi': -41,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}},
 'time': {'get_time': {'err_code': 0,
                       'hour': 16,
                       'mday': 1,
                       'min': 39,
                       'month': 1,
                       'sec': 54,
                       'year': 2000},
          'get_timezone': {'err_code': 0, 'index': 6}}}
Old time: 2000-01-01 16:39:54-08:00 (PST8PDT)
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"time":{"set_time":{"year":2026,"month":8,"mday":29,"hour":11,"min":30,"sec":30}}}
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/request
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query posted Host is 192.168.0.1, Sequence is -534551258, Response status is 200, Request was {"time":{"set_time":{"year":2026,"month":8,"mday":29,"hour":11,"min":30,"sec":30}}}
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query response received
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 << {'time': {'set_time': {'err_code': 0}}}
DEBUG:kasa.iot.iotdevice:Module <Module HomeKit (smartlife.iot.homekit) for 192.168.0.1> not supported, skipping
DEBUG:kasa.iot.iotdevice:Adding query for <Module Schedule (schedule) for 192.168.0.1>: {'schedule': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Usage (schedule) for 192.168.0.1>: {'schedule': {'get_realtime': {}, 'get_daystat': {'year': 2026, 'month': 8}, 'get_monthstat': {'year': 2026}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Antitheft (anti_theft) for 192.168.0.1>: {'anti_theft': {'get_rules': {}, 'get_next_action': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Time (time) for 192.168.0.1>: {'time': {'get_time': {}, 'get_timezone': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Cloud (cnCloud) for 192.168.0.1>: {'cnCloud': {'get_info': {}}}
DEBUG:kasa.iot.iotdevice:Adding query for <Module Led (system) for 192.168.0.1>: {}
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 >> {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":8},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.httpclient:Posting to http://192.168.0.1/app/request
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query posted Host is 192.168.0.1, Sequence is -534551257, Response status is 200, Request was {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2026,"month":8},"get_monthstat":{"year":2026}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},"cnCloud":{"get_info":{}}}
DEBUG:kasa.transports.klaptransport:Device 192.168.0.1 query response received
DEBUG:kasa.protocols.iotprotocol:192.168.0.1 << {'anti_theft': {'get_next_action': {'err_code': -2,
                                    'err_msg': 'member not support'},
                'get_rules': {'enable': 0,
                              'err_code': 0,
                              'rule_list': [],
                              'version': 2}},
 'cnCloud': {'get_info': {'binded': 0,
                          'cld_connection': 0,
                          'err_code': 0,
                          'fwDlPage': '',
                          'fwNotifyType': -1,
                          'illegalType': 0,
                          'server': 'n-devs.tplinkcloud.com',
                          'stopConnect': 0,
                          'tcspInfo': '',
                          'tcspStatus': 0,
                          'username': ''}},
 'schedule': {'get_daystat': {'day_list': [], 'err_code': 0},
              'get_monthstat': {'err_code': 0, 'month_list': []},
              'get_next_action': {'err_code': 0, 'type': -1},
              'get_realtime': {'err_code': -2, 'err_msg': 'member not support'},
              'get_rules': {'enable': 0,
                            'err_code': 0,
                            'rule_list': [],
                            'version': 2}},
 'system': {'get_sysinfo': {'active_mode': 'none',
                            'alias': '#MASKED_NAME#',
                            'dev_name': 'Smart Wi-Fi Plug Mini',
                            'deviceId': 'REDACTED_2DAB5A958F38BCCFF8352E51E114A5E',
                            'err_code': 0,
                            'feature': 'TIM',
                            'hwId': 'REDACTED_1DD892EA69AB42199F59E41',
                            'hw_ver': '5.0',
                            'icon_hash': '',
                            'latitude_i': 0,
                            'led_off': 0,
                            'lnk_on': 0,
                            'longitude_i': 0,
                            'mac': '00:5F:67:00:00:00',
                            'mic_type': 'IOT.SMARTPLUGSWITCH',
                            'model': 'HS103(US)',
                            'next_action': {'type': -1},
                            'obd_src': 'tplink',
                            'oemId': 'REDACTED_6FA93568D818524FE170CEC',
                            'on_time': 2394,
                            'relay_state': 1,
                            'rssi': -41,
                            'status': 'new',
                            'sw_ver': '1.1.3 Build 250908 Rel.112508',
                            'updating': 0}},
 'time': {'get_time': {'err_code': 0,
                       'hour': 11,
                       'mday': 29,
                       'min': 30,
                       'month': 8,
                       'sec': 30,
                       'year': 2026},
          'get_timezone': {'err_code': 0, 'index': 6}}}
New time: 2026-08-29 11:30:30-07:00 (PST8PDT)

mitchwebster commented Sep 4, 2026 •
edited
Loading

Copy link
Copy Markdown

I have two KL125 bulbs that stopped authenticating after a firmware update, with the familiar Device response did not match our challenge.

Their discovery response has no new_klap key at all — mgt_encrypt_schm is just {is_support_https: false, encrypt_type: "KLAP", http_port: 80, lv: 2}. I'm not sure #1648 and #1731 will cover this use case as these do not advertise new_klap: 1. It does look like #1731's login_version approach may solve this though.

Looking at the current PR, should we also handle the case where the encrypt_type has switched to KLAP but does not provide this field? Potentially reusing what's in #1731

Copy link
Copy Markdown

I have 2 HS200 (US wifi smart switch) that stopped working after I updated their firmware, I can confirm that pulling this change fixed my issues and they are now connected again.
Thanks for the amazing work!

LTKMN commented Sep 16, 2026

Copy link
Copy Markdown

+1, I bought brand new KL125 bulbs (hw 4.0, fw 1.1.1 Build 260710 Rel.082646 (discovery: encrypt_type: KLAP, lv: 2)) and they didn't connect for the reasons above: (AuthenticationError: Device response did not match our challenge)

connects as IotBulb over KlapTransportV2, and update() works. fix #1692 and #1731 seemingly also would work for this

re: the earlier comment that KL125s don't advertise new_klap: this bulb's port-20002 discovery response does include new_klap: 1 in mgt_encrypt_schm. it seemingly just drops the field when it parses the response, so it doesn't appear in output from master. this PR 1580's detection covers these bulbs, claude wrote me a local patch fix while this is being merged

twlim1 commented Sep 16, 2026

Copy link
Copy Markdown

Tested this branch against real new_klap hardware — works unmodified. Reporting since the PR mentions wanting field testing, and this covers a model and device family that isn't in the fixtures here.

Hardware: 2× Kasa KS200M(US) Motion-Sensor Light Switch, hw 1.0, fw 1.1.4 Build 241223 Rel.104700
Tested at: 71a4147

Result: auto-detection and transport selection both work

connection_type : family=IOT.SMARTPLUGSWITCH  enc=KLAP  login_version=2  new_klap=True
protocol        : IotProtocol / transport=KlapTransportV2     <- selected automatically
auth            : OK
class           : IotWallSwitch (DeviceType.WallSwitch)
modules         : Led, Time, ambient, anti_theft, cloud, motion, schedule, usage
features        : state, led, on_since, rssi, cloud_connection, reboot,
                  pir_enabled, pir_triggered, pir_range, pir_threshold,
                  pir_value, pir_percent, pir_adc_{min,mid,max,value},
                  ambient_light, ambient_light_enabled

On 0.10.2 both units fail with AuthenticationError: Device response did not match our challenge, and setting login_version=2 on DeviceConnectionParameters doesn't help because get_protocol() doesn't consult it — so the .NEW_KLAP key is what actually unblocks these.

The unconditional sysinfo lookup matters here

Worth calling out that the device_factory change — querying sysinfo for the precise subclass for all IotDevices rather than only on XorTransport — is load-bearing for this device, not just tidy-up. Without it the KS200M resolves from its family string to a generic plug; with it, it comes back as IotWallSwitch and the motion / ambient modules and the whole PIR feature set appear. A KLAP wall switch would otherwise be silently under-featured.

Test suite

22,543 passed, 852 skipped on the branch. I initially saw 4 failures in tests/iot/test_iottimezone.py and test_device_timezones, but those were ZoneInfoNotFoundError from my container lacking tzdata — installing it gives 14/14 passing. No real failures. The touched test modules (test_device_factory, test_discovery, test_deviceconfig) are 3508 passed.

End-to-end in Home Assistant

Also exercised through HA rather than just the library: installed the branch into a Home Assistant 2026.9.2 container (the branch reports 0.10.2, so it satisfies HA's python-kasa[speedups]==0.10.2 pin), added both switches through the normal tplink config flow, and got 14 entities across both devices — main switch, motion-sensor enable, LED, signal strength, on-since, cloud connection, restart. Verified write control by toggling and restoring, and the state read back correctly each time.

Happy to run anything else against these two, or to retest after further review changes.

Copy link
Copy Markdown

@mitchwebster adding a KL125 data point, plus a separate failure on mine that turns out not to be a transport problem at all.

As @LTKMN noted above, mine does advertise the field: port-20002 discovery returns 'mgt_encrypt_schm': {'ANS': False, 'encrypt_type': 'KLAP', 'http_port': 80, 'is_support_https': False, 'lv': 2, 'new_klap': 1} so your bulbs having no new_klap key at all is likely a firmware-revision difference rather than a model one.

Hardware: 1× Kasa KL125(US), hw 4.0, fw 1.1.1 Build 260710 Rel.082646
Tested at: 71a4147

Detection and transport selection are correct

device_factory  : Finding transport for IOT.KLAP.NEW_KLAP
class           : kasa.iot.iotbulb.IotBulb          <- correct, automatically
handshake1      : hashes match with expected credentials
handshake2      : complete

state succeeds about half the time

20 consecutive runs of kasa --host <ip> --debug state, 5 s apart: 10 passed, 10 failed (9 × Error trying to decrypt ... not a multiple of the block length, 1 × TimeoutError).

Both handshakes and the first two {"system":{"get_sysinfo":{}}} queries always succeed. The failure always lands on the third, larger query on /app/request.

The failing response isn't an encrypted payload, it's an HTML page

Logging the response length around KlapEncryptionSession.decrypt shows the same query returning 3264 bytes when it works and 49 bytes when it doesn't. Those 49 bytes are:

<html><body><center>200 OK</center></body></html>

So the bulb intermittently answers /app/request with a plain HTTP status page instead of a KLAP frame. decrypt() then slices the first 32 bytes off as the signature and tries to AES-decrypt er></body></html>, which is where the block-length error comes from.

That's a device-side fault, but it might be worth a cheap guard on our side: the response is only ever valid if len(body) > 32 and (len(body) - 32) % 16 == 0. Checking that before decrypt() and raising a retryable device error would turn a confusing crypto message into a clean retry, and this one is worth retrying, because the bulb recovers on its own.

The failures come in blocks rather than at random:

run:  1                   10                  20
      F F F F P P P P P F F F F F P P P P P F

Transitions land every ~28–29 s, so the bulb alternates between serving KLAP and serving that HTML page on roughly a 57 s cycle.

Control

Same loop, branch and credentials against an HS300 on the same LAN: 20 passed, 0 failed, 9 queries deep each time including all 6 children. Nothing wrong with the branch.

None of this should block the PR, detection is doing its job on this bulb. Posting in case the length guard is of interest, and happy to run anything else against it.

Debug excerpt from a failing run, and the raw response capture (redacted)
DEBUG:kasa.transports.klaptransport:Starting handshake with 10.0.0.247
DEBUG:kasa.httpclient:Posting to http://10.0.0.247/app/handshake1
DEBUG:kasa.transports.klaptransport:Handshake1 posted at 2026-09-17 09:20:14.186369. Host is 10.0.0.247, Response status is 200, Request was ab66ed063f25e5aba85a6353298112f6
DEBUG:kasa.transports.klaptransport:Handshake1 success at 2026-09-17 09:20:14.186415. Host is 10.0.0.247, Server remote_seed is: e3f8ee0f9db96d0edc06ec0fe3a66d0f, server hash is: 9634b77bf570bdb70b669f8f8c5d78b68063530c96d16d1a8ffa36efb5ac997d
DEBUG:kasa.transports.klaptransport:handshake1 hashes match with expected credentials
DEBUG:kasa.httpclient:Posting to http://10.0.0.247/app/handshake2
DEBUG:kasa.transports.klaptransport:Handshake2 posted 2026-09-17 09:20:14.196090. Host is 10.0.0.247, Response status is 200, Request was e7e4bafc3bdda0793bb9167bb4000192486560a6436c12d46da989909186f62b
DEBUG:kasa.transports.klaptransport:Handshake with 10.0.0.247 complete
DEBUG:kasa.httpclient:Posting to http://10.0.0.247/app/request
DEBUG:kasa.transports.klaptransport:Device 10.0.0.247 query posted Host is 10.0.0.247, Sequence is -603044004, Response status is 200, Request was {"system":{"get_sysinfo":{}}}
DEBUG:kasa.transports.klaptransport:Device 10.0.0.247 query response received
        ... second get_sysinfo query succeeds (seq -603044003) ...
DEBUG:kasa.transports.klaptransport:Device 10.0.0.247 query posted Host is 10.0.0.247, Sequence is -603044002, Response status is 200, Request was {"system":{"get_sysinfo":{}},"smartlife.iot.homekit":{"setup_info_get":{}},"smartlife.iot.common.emeter":{"get_realtime":{},"get_daystat":{"year":2026,"month":9},"get_monthstat":{"year":2026}},"smartlife.iot.common.schedule":{"get_rules":{},"get_next_action":{}}}
DEBUG:kasa.protocols.iotprotocol:Unable to query the device: 10.0.0.247, not retrying: Error trying to decrypt device 10.0.0.247 response: The length of the provided data is not a multiple of the block length.
Raised error: Error trying to decrypt device 10.0.0.247 response: The length of the provided data is not a multiple of the block length.

Instrumented around KlapEncryptionSession.decrypt, verbatim from one run, a failing attempt followed by a passing one:

  --- attempt 3
    [OK ] ciphertext_len=1120
    [OK ] ciphertext_len=1120
    [BAD] total=49 ciphertext_len=17
    raw_full_hex   = 3c68746d6c3e3c626f64793e3c63656e7465723e323030204f4b3c2f63656e7465723e3c2f626f64793e3c2f68746d6c3e
    body_after_sig = b'er></body></html>'
  attempt 3: FAIL KasaException
  --- attempt 10
    [OK ] ciphertext_len=1120
    [OK ] ciphertext_len=1120
    [OK ] ciphertext_len=3232
    [OK ] ciphertext_len=1520
  attempt 10: OK

(raw_full_hex decodes to <html><body><center>200 OK</center></body></html>.)


Separately, confirming the rest of my KLAP/lv: 2/new_klap: 1 devices all authenticate on this branch, none of which appear in the fixtures here:

Model HW Firmware Result
HS300(US) ×2 2.0 1.1.2 Build 241220 Rel.171333 OK, all 6 children enumerated on each
EP10(US) ×2 1.0 1.1.1 Build 250908 Rel.112508 OK
EP40(US) 1.0 1.1.0 Build 250225 Rel.171723 OK, both sockets

All six fail on 0.10.2 with AuthenticationError: Device response did not match our challenge. Only the KL125 shows the intermittent HTML-response behaviour above; the other five are stable.

Copy link
Copy Markdown

Real-world HS300 / Home Assistant testing

I tested this PR against multiple real Kasa HS300(US) HW 2.0 strips, both directly with python-kasa and through Home Assistant.

Tested commit:

71a4147984aedc7cfa9ce4613204ecf28b15dac7

Environment:

  • Home Assistant 2026.8.3 (Container)
  • Python 3.14
  • python-kasa reports version 0.10.2 at this commit
  • HS300(US), hardware 2.0
  • Firmware on the device investigated most closely: 1.1.2 Build 241220 Rel.171333

The affected devices advertise discovery information including:

device_type: IOT.SMARTPLUGSWITCH
encrypt_type: KLAP
lv/login_version: 2
new_klap: 1

Results

With this PR installed, the HS300s correctly:

  • Select IotProtocol with KlapTransportV2
  • Refine the generic IOT.SMARTPLUGSWITCH discovery result to IotStrip
  • Discover all six outlets as IotStripPlug children
  • Preserve stable child IDs for the six outlets
  • Expose per-outlet energy monitoring
  • Read device/outlet state successfully
  • Control individual outlet relays successfully

For example, direct discovery of one previously unused HS300 produced:

Python class : IotStrip
Protocol     : IotProtocol
Transport    : KlapTransportV2
Model        : HS300
Child count  : 6

All six children were present, and a direct relay test successfully toggled an outlet off and restored it.

Home Assistant testing

I also force-installed this commit over Home Assistant's stock python-kasa==0.10.2 and tested the normal TP-Link integration.

Results across several HS300s:

  • Existing HS300 config entries recovered and exposed their six child outlets correctly.
  • Existing outlet entities and automations continued working.
  • An existing HS300 that required reauthentication successfully completed Home Assistant's Reconfigure flow without losing its existing child entities or automations.
  • A previously unused HS300 was discovered/configured through the normal Home Assistant TP-Link config flow and appeared correctly as a six-outlet strip rather than one generic plug.
  • Relay commands issued through Home Assistant worked normally.

I also ended up factory-resetting and reprovisioning one HS300 while investigating an apparently unrelated intermittent connectivity problem.

After the factory reset, Tapo-side configuration/names had been lost. Home Assistant automatically rediscovered the physical strip and reassociated its six outlets with the existing entity-registry entries. The previous Home Assistant entity names returned rather than the new outlet names assigned during Tapo provisioning.

So the child/device identity remained sufficiently stable across a factory reset to preserve the existing HA entities in this test.

Intermittent timeout investigated separately

One HS300 initially appeared to expose another problem: the initial modular update would occasionally time out.

I broke the update request apart and verified that every individual query worked, including:

  • system.get_sysinfo
  • anti-theft queries
  • schedule queries
  • day/month statistics
  • time/timezone
  • countdown
  • cnCloud.get_info
  • HomeKit setup-info query

I then tested the complete combined modular request directly, and it also succeeded.

Repeated fresh-session testing showed that the device itself was behaving intermittently rather than failing on a particular request.

Before factory reset/reprovisioning:

Successes : 15/20
Failures  : 5/20
Query min : 0.116s
Query avg : 0.798s
Query max : 3.305s

After factory reset/reprovisioning, using the same test and same complete request:

Successes : 20/20
Failures  : 0/20
Query min : 0.032s
Query avg : 0.243s
Query max : 0.703s

The device was also intermittently unavailable from the Tapo application before being reset.

Based on that testing, I do not believe the intermittent timeout was caused by this PR or by the combined modular query.

Overall

From my testing, this PR successfully resolves both issues I was seeing with these newer HS300 HW2 devices:

  1. Selecting the appropriate KLAP v2 transport for devices advertising new_klap: 1 / login version 2.
  2. Correctly refining the generic IoT discovery result into IotStrip, allowing Home Assistant/python-kasa to expose the six individual outlets rather than treating the HS300 as one generic plug.

I've now tested this with multiple HS300 HW2 strips, including direct python-kasa discovery/query/control, existing Home Assistant config entries, Home Assistant reauthentication, and fresh Home Assistant discovery/configuration.

Happy to collect additional debug output or generate a device fixture from one of these HS300s if that would be useful.

rytilahti modified the milestones: 0.11, 0.12 Oct 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IOT devices with KLAP encryption and login_version=2 use wrong transport (KlapTransport instead of KlapTransportV2)

Back | FazBrowse Home | New Git URL