Reputation: 1
I'm trying to work with: softScheck/tplink-smartplug
I'm stuck in a loop of errors. The fix for the first, causes the other, and the fix for the other, causes the first. The code is all found in tplink-smartplug.py at the git link.
cmd = "{\"system\":{\"set_relay_state\":{\"state\":0}}}"
sock_tcp.send(encrypt(cmd))
def encrypt(string):
key = 171
result = "\0\0\0\0"
for i in string:
a = key ^ ord(i)
key = a
result += chr(a)
return result
As it is, result = 'Ðòøÿ÷Õï¶Å Ôùðè·Ä°Ñ¥ÀâØ£òçöÔîÞ£Þ' and I get the error (on line 92 in original file: sock_tcp.send(encrypt(cmd)):
a bytes-like object is required, not 'str'
so I change the function call too:
sock_tcp.send(encrypt(cmd.encode('utf-8')))
and my error changes too:
ord() expected string of length 1, but int found
I understand what ord() is trying to do, and I understand the encoding. But what I don't understand is...how am I supposed to send this encrypted message to my smart plugin, if I can't give the compiler what it wants? Is there a work around? I'm pretty sure the original git was written in Python 2 or earlier. So maybe I'm not converting to Python 3 correctly?
Thanks for reading, I appreciate any help.
Upvotes: 0
Views: 2065
Reputation: 366213
In Python 2, the result of encode
is a str
byte-string, which is a sequence of 1-byte str
values. So, when you do for i in string:
, each i
is a str
, and you have to call ord(i)
to turn it into a number from 0 to 255.
In Python 3, the result of encode
is a bytes
byte-string, which is a sequence of 1-byte integers. So when you do for i in string:
, each i
is already an int
from 0 to 255, so you don't have to do anything to convert it. (And, if you try to do it anyway, you'll get the TypeError
you're seeing.)
Meanwhile, you're building result
up as a str
. In Python 2, that's fine, but in Python 3, that means it's Unicode, not bytes. Hence the other TypeError
you're seeing.
For more details on how to port Python 2 string-handling code to Python 3, you should read the Porting Guide, especially Text versus binary data, and maybe the Unicode HOWTO if you need more background.
One way you can write the code to work the same way for both Python 2 and 3 is to use a bytearray
for both values:
def encrypt(string):
key = 171
result = bytearray(b"\0\0\0\0")
for i in bytearray(string):
a = key ^ i
key = a
result.append(a)
return result
cmd = u"{\"system\":{\"set_relay_state\":{\"state\":0}}}"
sock_tcp.send(encrypt(cmd.encode('utf-8')))
Notice the u
prefix on cmd
, which makes sure it's Unicode even in Python 2, and the b
prefix on result
, which makes sure it's bytes even in Python 3. Although since you know cmd
is pure ASCII, it might be simpler to just do this:
cmd = b"{\"system\":{\"set_relay_state\":{\"state\":0}}}"
sock_tcp.send(encrypt(cmd))
If you don't care about Python 2, you can just for for i in string:
without converting it to a bytearray
, but you still probably want to use one for result
. (Being able to append
an int directly to it makes for simpler code—and it's even more efficient, as a nice bonus.)
Upvotes: 1