8000 Adding support for the new encryption protocol (updated version) by mystcb · Pull Request #267 · python-kasa/python-kasa · GitHub
[go: up one dir, main page]

Skip to content

Adding support for the new encryption protocol (updated version) #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from

Conversation

mystcb
Copy link
@mystcb mystcb commented Dec 7, 2021

Taking off where #117 left off, I have been making some additional amendments to make this work. As it stands this version is very rough, but completes some of the additions that @SimonWilkinson made from his branch.

During testing, I made a number of commits that went backwards and forwards in an attempt to get this working with HomeAssistant (only to find my dev setup was at fault). So I have tried to squash those changes into a single commit.

This version also merges changes made in the current master branch made yesterday, so hopefully as up to date as I can get it to that.

From the work Simon put in, this version should also fix the single device discovery issue as well.

Thank you Simon for doing most of the work, and apologies for the roughness of this PR!

Fixes #170 fixes #115

SimonWilkinson and others added 20 commits November 17, 2020 14:41
This adds support for the new TP-Link discovery and encryption
protocols. It is currently incomplete - only devices without
username and password are current supported, and single device
discovery is not implemented.

Discovery should find both old and new devices. When accessing
a device by IP the --klap option can be specified on the command
line to active the new connection protocol.
Restore the incorrectly commented out old discovery mechanism, so
both old and new devices are found.
Add the --user and --password command line options which can
be used to provide a username and password to authenticate to
the smart device using the KLAP protocol.

Check the 'owner' field in the discovery packet, and only attempt
to authenticate to devices for which we have a password.
Add a new class which holds authentication information for the
KLAP protocol
Making an emeter request against an HS100 plug crashes it.

So don't.
@mystcb mystcb marked this pull request as ready for review December 7, 2021 20:54
@shepwalker
Copy link

👋 What's needed to get this reviewed/merged? Anything I can do to help test? Also, is there any documentation on how to monkeypatch hassio, i'm happy to get this branch running on my install.

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.

Thanks for reviving the PR and working on this! I added some comments inline, as a general comment we are going to need unittests for the implementation.

API-wise it might be better if the KAP would not inherit from the TPLinkSmartHomeProtocol, but that the given protocol (be it the xor one or klap) would be used as necessary by _encrypt and _decrypt. This would simplify the protocol initialization to always be TPLinkSmartHomeProtocol(host, authentication=None) which opens it for future encryption protocols, if the need ever comes up.

What do you think, does that make sense?

Comment on lines +307 to +317
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
loop = asyncio.get_event_loop()

async def _on_device(dev):
await dev.update()
_LOGGER.info("Got device: %s", dev)

devices = loop.run_until_complete(Discover.discover(on_discovered=_on_device))
for ip, dev in devices.items():
print(f"[{ip}] {dev}")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
loop = asyncio.get_event_loop()
async def _on_device(dev):
await dev.update()
_LOGGER.info("Got device: %s", dev)
devices = loop.run_until_complete(Discover.discover(on_discovered=_on_device))
for ip, dev in devices.items():
print(f"[{ip}] {dev}")

Let's remove this. If needed, we should improve the kasa discover instead of this.

Comment on lines +2 to +10

Encryption/Decryption methods based on the works of
Lubomir Stroetmann and Tobias Esser

https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/
https://github.com/softScheck/tplink-smartplug/

which are licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this file is anymore based on the work done in those sources :-)

local_hash.hex(),
server_hash.hex(),
)
raise SmartDeviceException("Server response doesn't match our challenge")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
raise SmartDeviceException("Server response doesn't match our challenge")
raise SmartDeviceException("Device response doesn't match our challenge")

server_hash.hex(),
)
raise SmartDeviceException("Server response doesn't match our challenge")
else:
Copy link
Member

Choose a reason for hiding this comment

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

Unnecessary else, either output that to debug log without it, or remove if it's not useful to have around.

elif bulb:
dev = SmartBulb(host)
elif plug:
dev = SmartPlug(host)
dev = SmartPlug(host, authentication)
Copy link
Member

Choose a reason for hiding this comment

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

The authentication should probably be passed to all of these? Please rebase and skip the authentication for the deprecated flags (see https://github.com/python-kasa/python-kasa/blob/master/kasa/cli.py#L99).

@@ -205,13 +205,13 @@ async def erase_emeter_stats(self):
@requires_update
def emeter_this_month(self) -> Optional[float]:
"""Return this month's energy consumption in kWh."""
return sum([plug.emeter_this_month for plug in self.children])
return sum(plug.emeter_this_month for plug in self.children)
Copy link
Member

Choose a reason for hiding this comment

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

This are unrelated changes, but they should disappear when you rebase (the master has this already), so please rebase.

Comment on lines +144 to +146
if resp.status == 403:
self.handshake_done = False

Copy link
Member

Choose a reason for hiding this comment

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

Should this raise an exception, or is it expected to fall through to the decryption procedures?

Comment on lines +280 to +293
def _get_new_device_class(info: dict) -> Type[SmartDevice]:
"""Find SmartDevice subclass given new discovery payload."""
if "result" not in info:
raise SmartDeviceException("No 'result' in discovery response")

if "device_type" not in info["result"]:
raise SmartDeviceException("No 'device_type' in discovery result")

dtype = info["result"]["device_type"]

if dtype == "IOT.SMARTPLUGSWITCH":
return SmartPlug

raise SmartDeviceException("Unknown device type: %s", dtype)
Copy link
Member

Choose a reason for hiding this comment

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

I don't like the fact that there are two separate paths for the discovery, we should find a way to somehow generalize the code to handle model detection for both discovery protocols.

Comment on lines +241 to +247
if device_class is not None:
if authentication is None:
dev = device_class(host)
else:
dev = device_class(host, authentication)
await dev.update()
return dev
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
9E81
if device_class is not None:
if authentication is None:
dev = device_class(host)
else:
dev = device_class(host, authentication)
await dev.update()
return dev
dev = device_class(host, authentication=authentication)
await dev.update()

It's better to keep the original code, and just add keyword-only authentication parameter to SmartDevice class.

# In theory we should verify the hmac here too
return Padding.unpad(cipher.decrypt(payload[32:]), AES.block_size)

async def _ask(self, request: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

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

This is not being called at all?

@leoskyrocker
Copy link

hey thanks for the PR!
I'm under the impression that this will provide a fix for the HS110 FW 1.1.0, which is why I'd be super interested to contribute to this. Is there anything I can help with in the PR, or @mystcb are you planning to push some changes soon?

Thanks!

@threebrooks
Copy link

Was this PR (and Simon's) reviewed to death? It's a shame it never went in, this is an incredibly useful tool.

@shawngmc
Copy link
shawngmc commented Apr 4, 2023

Is there anyone who can look into this readthedocs error? I'd really like to start using this library for a few things with newer devices, like a KL420V5 light strip...

@zonque
Copy link
zonque commented Jun 18, 2023

@mystcb Thanks a lot for your efforts! Are you still working on pushing this over the finish line?

@mystcb
Copy link
Author
mystcb commented Jul 13, 2023

Hi all,

Apologies for the delay in responding to this - between a move of a house and using a different set of remote plugs I lost track of this completely.

What I do have is the broken plugs next to me, so I will take a look and see what needs to be done - I realise that a lot of the code is now very out of date from the main branch, so will need to look into that and merge.

If someone else is already looking at this, please let me now - as I will need to remember how to get this up to date and such! But I will take another look, and see if I can this set of plugs I have sitting next to me working

Colin

@mystcb
Copy link
Author
mystcb commented Jul 13, 2023

Actually, just spotted that another PR is already open with some of the changes in

#477

Going to have a look through them now.

@mystcb
Copy link
Author
mystcb commented Aug 31, 2023

I will close this off as @sdb9696 has a new PR open #477 for this and has done a lot of work! Thank you!

@mystcb mystcb closed this Aug 31, 2023
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.

Support for EP10 Plug Implement the new protocol (HTTP over 80/tcp, 20002/udp for discovery)
8 participants
0