8000 Implement KlapTransportV2 for new_klap by ZeliardM · Pull Request #1580 · python-kasa/python-kasa · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@ZeliardM
Copy link
Contributor
@ZeliardM ZeliardM commented Sep 29, 2025

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
Copy link
codecov bot commented Sep 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.06%. Comparing base (a87926f) to head (87b6741).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1580      +/-   ##
==========================================
+ Coverage   92.82%   93.06%   +0.23%     
==========================================
  Files         157      158       +1     
  Lines        9649     9712      +63     
  Branches      976      985       +9     
==========================================
+ Hits         8957     9038      +81     
+ Misses        492      480      -12     
+ Partials      200      194       -6     

☔ 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.

@ZeliardM
Copy link
Contributor 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.

@ZeliardM
Copy link
Contributor 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
Copy link
Contributor Author
ZeliardM commented Oct 1, 2025

@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
Copy link
Copilot AI left a comment

Choose a reason for hiding this comment

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

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
Copy link
Contributor Author
ZeliardM commented Oct 2, 2025

@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.

Copy link
Member
@rytilahti rytilahti left a comment

Choose a reason for hiding this comment

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

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

@ZeliardM ZeliardM changed the title Implement KlapTransportV3 for new_klap Implement KlapTransportV2 for new_klap Oct 7, 2025
@ZeliardM
Copy link
Contributor Author
ZeliardM commented Oct 7, 2025

@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.

Copy link
Member
@rytilahti rytilahti left a comment

Choose a reason for hiding this comment

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

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!

@rytilahti rytilahti added the enhancement New feature or request label Oct 11, 2025
@rytilahti rytilahti added this to the 0.11 milestone Oct 11, 2025
@Hashcode
Copy link

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!

@ZeliardM ZeliardM changed the title Implement KlapTransportV2 for new_klap Implement new_klap Connection Parameter Oct 31, 2025
@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 1, 2025

@rytilahti How would you like to handle the CLI issues with this? Looking at the code for the CLI, it seems like the devices that use new_klap are older legacy iot devices but it makes them use the newer connection methods used by smart devices. It seems like the way the legacy devices are handled in the CLI and this new way are conflicting.

@ZeliardM ZeliardM changed the title Implement new_klap Connection Parameter Implement KlapTransportV2 for new_klap Nov 4, 2025
@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 6, 2025

Ok, I will also have to look at updating some other pieces and tests to handle the new fixture files that are created with this new_klap feature as I did not initially and now see some problems with those as well. I will look over everything that came back in testing today and try to update everything here accordingly.

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 7, 2025

@Hashcode I think I am close to getting this resolved. I will try to get everything pushed out tomorrow for you to test if you don't mind.

@rytilahti I added the test fixture here in this PR instead of a separate one because there were changes with the discover process and everything that were done with the new_klap that were required for the tests to work correctly with the fixture so I figured it would be best to have everything in here. I am making changes to the discover process as well, I want you to take a look and let me know. It may require some changes for contributing guidelines.

@Hashcode
Copy link
Hashcode commented Nov 7, 2025

@Hashcode I think I am close to getting this resolved. I will try to get everything pushed out tomorrow for you to test if you don't mind.

Sounds great! ping me whenever you're ready and I'll give it a go.

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 7, 2025

Ok, looks good on the tests and coverage.

@Hashcode Give this new stuff a shot and let me know.

@rytilahti If the testing by Hascode works out, then I believe we are finally good to go on this one.

@Hashcode
Copy link
Hashcode commented Nov 7, 2025

Ok, looks good on the tests and coverage.

@Hashcode Give this new stuff a shot and let me know.

Hi @ZeliardM ,
I'm still seeing a connection 111 error when I add --type strip to my normal command.

$ uv run kasa --type strip --host 192.168.101.79 --username "xxx@xxx.com" --password "xxx" device state
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Using CPython 3.13.7 interpreter at: /usr/bin/python3
Creating virtual environment at: .venv
      Built python-kasa @ file:///<location>/python-kasa
Installed 56 packages in 10ms
Raised error: Unable to connect to the device: 192.168.101.79:9999: [Errno 111] Connect call failed ('192.168.101.79', 9999)
Run with --debug enabled to see stacktrace

vs. w/o the strip type:

$ uv run kasa --host 192.168.101.79 --username "xxx@xxx.com" --password "xxx" device state
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Discovering device 192.168.101.79 for 10 seconds
== TP-LINK_Power Strip_2D16 - HS300 ==
Host: 192.168.101.79
Port: 80
Device state: False
Time:         2025-11-07 15:29:55-08:00 (tz: PST8PDT)
Hardware:     2.0 (US)
Firmware:     1.1.2 Build 241220 Rel.171333
<snip>

I did clean out the .venv files and ran uv cache clean to try and make sure I'm running the current branch from feature/new-klap. The tip SHA is: e9fc5dc ("Update discover and tests for new fixtures")

Let me know what kind of debug information you'd like.

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 7, 2025

@Hashcode If you can pull the debug for the failed command, may help in finding where it's failing for me. The other commands that were failing are working now?

@Hashcode
Copy link
Hashcode commented Nov 8, 2025

@Hashcode If you can pull the debug for the failed command, may help in finding where it's failing for me. The other commands that were failing are working now?

Discovery works great (and was working in the previous version of the PR). The connection 111 error happens when --type is passed in as a parameter and somehow the new KLAP protocol isn't used for authentication.

@Hashcode
Copy link
Hashcode commented Nov 8, 2025

@ZeliardM When I use the --type strip parameter, this is the debug log for the error:

NOTE: I see xortransport.py being shown below and I think this should be using klaptransport.py

Raised error: Unable to connect to the device: 192.168.101.79:9999: [Errno 111] Connect call failed ('192.168.101.79', 9999)
Traceback (most recent call last):
  File "<redacted>/python-kasa/kasa/transports/xortransport.py", line 129, in send
    await self._connect(self._timeout)
  File "<redacted>/python-kasa/kasa/transports/xortransport.py", line 67, in _connect
    self.reader, self.writer = await task
                               ^^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/streams.py", line 48, in open_connection
    transport, _ = await loop.create_connection(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        lambda: protocol, host, port, **kwds)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/base_events.py", line 1171, in create_connection
    raise exceptions[0]
  File "/usr/lib/python3.13/asyncio/base_events.py", line 1146, in create_connection
    sock = await self._connect_sock(
           ^^^^^^^^^^^^^^^^^^^^^^^^^
        exceptions, addrinfo, laddr_infos)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/base_events.py", line 1045, in _connect_sock
    await self.sock_connect(sock, address)
  File "/usr/lib/python3.13/asyncio/selector_events.py", line 641, in sock_connect
    return await fut
           ^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/selector_events.py", line 681, in _sock_connect_cb
    raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 111] Connect call failed ('192.168.101.79', 9999)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<redacted>/python-kasa/.venv/bin/kasa", line 10, in <module>
    sys.exit(cli())
             ~~~^^
  File "<redacted>/python-kasa/kasa/cli/common.py", line 282, in __call__
    asyncio.run(self.main(*args, **kwargs))
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "/usr/lib/python3.13/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/usr/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "<redacted>/python-kasa/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1126, in main
    rv = await self.invoke(ctx)
         ^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/cli/common.py", line 270, in invoke
    _handle_exception(self._debug, exc)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/cli/common.py", line 268, in invoke
    return await super().invoke(ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1747, in invoke
    await super().invoke(ctx)
  File "<redacted>/python-kasa/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 1496, in invoke
    return await ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/.venv/lib/python3.13/site-packages/asyncclick/core.py", line 829, in invoke
    rv = await rv
         ^^^^^^^^
  File "<redacted>/python-kasa/kasa/cli/main.py", line 370, in cli
    await dev.update()
  File "<redacted>/python-kasa/kasa/iot/iotstrip.py", line 144, in update
    await super().update(update_children)
  File "<redacted>/python-kasa/kasa/iot/iotdevice.py", line 315, in update
    response = await self.protocol.query(req)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/protocols/iotprotocol.py", line 86, in query
    return await self._query(request, retry_count)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/protocols/iotprotocol.py", line 137, in _query
    raise ex
  File "<redacted>/python-kasa/kasa/protocols/iotprotocol.py", line 91, in _query
    return await self._execute_query(request, retry)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/protocols/iotprotocol.py", line 151, in _execute_query
    resp = await self._transport.send(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/python-kasa/kasa/transports/xortransport.py", line 138, in send
    raise KasaException(
        f"Unable to connect to the device: {self._host}:{self._port}: {ex}"
    ) from ex
kasa.exceptions.KasaException: Unable to connect to the device: 192.168.101.79:9999: [Errno 111] Connect call failed ('192.168.101.79', 9999)

@Hashcode
Copy link
Hashcode commented Nov 8, 2025

@ZeliardM Ah, I think we are missing a way of setting new_klap here in the cli DeviceConnectionParams:
https://github.com/ZeliardM/python-kasa/blob/feature/new-klap/kasa/cli/main.py#L329

If we have that then I can run a command like this to avoid discovery:

kasa --type smart --device-family "IOT.SMARTPLUGSWITCH" --host 192.168.101.79 --username "xxx@xxx.com" --password "xxx" device --index 0 state

I tested this w/ a small diff to see if it worked:

diff --git a/kasa/cli/main.py b/kasa/cli/main.py
index 4f1eccd..37b3613 100755
--- a/kasa/cli/main.py
+++ b/kasa/cli/main.py
@@ -331,6 +331,7 @@ async def cli(
             DeviceEncryptionType(encrypt_type),
             login_version,
             https,
+            new_klap=True,
         )
         config = DeviceConfig(
             host=host,

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 8, 2025

@Hashcode Ok, now I know where I need to look. Let me poke around a little more. Thanks!

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Nov 8, 2025

@Hashcode @rytilahti Ok, so after looking through everything, the issue is new_klap for sure. The --type flag does not any discover parts to query the device to get the connection parameters, it builds them based on the type sent and then uses them to get the protocol, the problem is that new_klap could be on different types of devices and has up until now been determined after querying the device. I will look and see if there is a way to do so inside of all of this to get new_klap before we send the connect piece and let you know.

@ZeliardM
Copy link
Contributor Author

@Hashcode @rytilahti Ok, so, the only way I can figure out how to do this without initially discovering the device via the IP and then pulling if it has new_klap is to have the user specify the encrypt_type as well. The user would use "KLAPV2" and that would be flagged as new_klap for the DeviceConnectionParameters and that would allow the device to connect like it's supposed to. I am open to any other thought, questions, or concerns here.

@Hashcode
Copy link

@Hashcode @rytilahti Ok, so, the only way I can figure out how to do this without initially discovering the device via the IP and then pulling if it has new_klap is to have the user specify the encrypt_type as well. The user would use "KLAPV2" and that would be flagged as new_klap for the DeviceConnectionParameters and that would allow the device to connect like it's supposed to. I am open to any other thought, questions, or concerns here.

That's pretty much what I expected. Sounds good from my end.

@rytilahti
Copy link
Member

I also think that's fair, if I were to guess, this naming was not even supposed to be mainlined, so it might only concern very few devices and users.

We can also always revise later, if necessary :-)

@ZeliardM
Copy link
Contributor Author

Ok, sounds good. I will see if there is a way to add something about failing to connect to try KLAPV2 for encrypt_type, will see what I can put together and push out.

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Dec 9, 2025

@rytilahti @Hashcode Ok, I have added in the CLI --encrypt-type KLAPV2 for this and that should resolve the remaining CLI issues. Let me know what you think and if we're good, this should be good to merge now.

2A39

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Dec 9, 2025

Found a few errors while testing, this last commit should fix them. @Hashcode Again, give it a test and let me know. @rytilahti Let me know when you want to proceed with this one, I am still working on the TPAP implementation, I think it should be in a new version with this one.

@Hashcode
Copy link

@ZeliardM Sorry for the delay. I was out all last week with the flu.

From my tests, this solves the issues I was seeing:

uv run kasa --type smart --device-family "IOT.SMARTPLUGSWITCH" --encrypt-type KLAPV2 --host $KASA_SERVER --credentials-hash "$KASA_CREDENTIALS_HASH" device --index 0 state

Targeting child device Plug 1
== Plug 1 - Socket for HS300(US) ==
Host: ###.###.###.###
Port: 80
Device state: True
Time:         2025-12-15 12:49:25-08:00 (tz: PST8PDT)
Hardware:     2.0 (US)
Firmware:     1.1.2 Build 241220 Rel.171333
MAC (rssi):   ##:##:##:##:##:## (-27)

== Primary features ==
State (state): True
Current consumption (current_consumption): 0.0 W
Voltage (voltage): 126.2 V
Current (current): 0.0 A

== Information ==
On since (on_since): 2025-11-11 12:47:24-08:00
Today's consumption (consumption_today): 0.0 kWh
This month's consumption (consumption_this_month): 0.0 kWh
Total consumption since reboot (consumption_total): 1.237 kWh

== Configuration ==

== Debug ==

Thanks for the hard work on this!

@ZeliardM
Copy link
Contributor Author
ZeliardM commented Dec 15, 2025

@Hashcode Excellent, thanks! @rytilahti This looks like it's good to go if you are.

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.

3 participants

0