-
-
Notifications
You must be signed in to change notification settings - Fork 504
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from kueblc/smart-link-py
SmartConfig in Python
- Loading branch information
Showing
9 changed files
with
232 additions
and
78 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
""" | ||
broadcast.py | ||
Created by kueblc on 2019-01-25. | ||
Encode data for Tuya smartconfig via broadcast | ||
broadcast strategy ported from https://github.com/tuyapi/link | ||
""" | ||
|
||
from crc import crc_8 | ||
|
||
broadcast_head = [1, 3, 6, 10] | ||
|
||
def encode_broadcast_body( password, ssid, token_group ): | ||
r = [] | ||
r.append( len(password) ) | ||
r.extend([ ord(l) for l in password ]) | ||
r.append( len(token_group) ) | ||
r.extend([ ord(l) for l in token_group ]) | ||
r.extend([ ord(l) for l in ssid ]) | ||
e = [] | ||
length = len(r) | ||
length_crc = crc_8([ length ]) | ||
e.append( length >> 4 | 16 ) | ||
e.append( length & 0xF | 32 ) | ||
e.append( length_crc >> 4 | 48 ) | ||
e.append( length_crc & 0xF | 64 ) | ||
sequence = 0 | ||
for i in range(0, length, 4): | ||
group = [] | ||
group.append( sequence ) | ||
group.extend( r[ i : i+4 ] ) | ||
group.extend( [0] * (5 - len(group)) ) | ||
group_crc = crc_8(group) | ||
e.append( group_crc & 0x7F | 128 ) | ||
e.append( sequence | 128 ) | ||
e.extend([ b | 256 for b in r[ i : i+4 ] ]) | ||
sequence += 1 | ||
e.extend( [256] * (length - i) ) | ||
return e | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
""" | ||
crc.py | ||
Created by kueblc on 2019-01-25. | ||
Compute 8bit and 32bit CRCs for Tuya communication | ||
based in part on https://github.com/tuyapi/link | ||
""" | ||
|
||
def crc_8_byte( b ): | ||
r = 0 | ||
for i in range(8): | ||
if (r ^ b) & 1: | ||
r ^= 0x18 | ||
r >>= 1 | ||
r |= 0x80 | ||
else: | ||
r >>= 1 | ||
b >>= 1 | ||
return r | ||
|
||
def crc_8( a ): | ||
r = 0 | ||
for b in a: | ||
r = crc_8_byte( r ^ b ) | ||
return r | ||
|
||
crc_32_table = [0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261, 249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247, 498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657, 325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979, 997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853, 901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631, 651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993, 671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731, 1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237, 2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999, 1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073, 1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891, 1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301, 1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647, 1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281, 1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307, -306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523, -480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377, -267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879, -19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637, -690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859, -670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161, -882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815, -977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221, -1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371, -1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881, -2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567, -2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701, -1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035, -1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897, -1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431, -1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117] | ||
|
||
def crc_32( a ): | ||
r = -1 | ||
for b in a: | ||
r = ( (r & 0xFFFFFFFF) >> 8 ) ^ crc_32_table[(r ^ b) & 255] | ||
return r ^ -1 | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
""" | ||
main.py | ||
Created by kueblc on 2019-01-25. | ||
Configure Tuya devices via smartconfig for tuya-convert | ||
""" | ||
|
||
ssid = "vtrust-flash" | ||
passwd = "flashmeifyoucan" | ||
region = "US" | ||
token = "00000000" | ||
secret = "0101" | ||
|
||
from smartconfig import smartconfig | ||
|
||
print('Put Device in Learn Mode! Sending SmartConfig Packets now') | ||
|
||
print('Sending SSID '+ssid) | ||
print('Sending wifiPassword '+passwd) | ||
print('SmartConfig in progress') | ||
|
||
smartconfig( passwd, ssid, region, token, secret ) | ||
|
||
print() | ||
print('SmartConfig complete.') | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
""" | ||
multicast.py | ||
Created by kueblc on 2019-01-25. | ||
Encode data for Tuya smartconfig via multicast | ||
multicast strategy reverse engineered by kueblc | ||
""" | ||
|
||
from crc import crc_32 | ||
|
||
from Crypto.Cipher import AES | ||
pad = lambda data, block_size : data + ('\0' * ( (block_size - len(data)) % block_size ) ) | ||
aes = AES.new( "a3c6794oiu876t54", AES.MODE_ECB, '' ) | ||
encrypt = lambda data : aes.encrypt( pad(data, 16) ) | ||
|
||
def encode_pw( pw ): | ||
r = [] | ||
pw_bytes = [ ord(c) for c in pw ] | ||
encrypted_pw = encrypt(pw) | ||
# CRC and length are from plaintext | ||
crc = crc_32(pw_bytes) | ||
# length, twice | ||
r.append( len(pw) ) | ||
r.append( len(pw) ) | ||
# encode CRC as LE | ||
r.extend([ (crc >> i) & 255 for i in range(0,32,8) ]) | ||
# payload, AES encrypted | ||
r.extend( encrypted_pw ) | ||
return r | ||
|
||
def encode_plain( s ): | ||
r = [] | ||
s_bytes = [ ord(c) for c in s ] | ||
crc = crc_32(s_bytes) | ||
# length, twice | ||
r.append( len(s) ) | ||
r.append( len(s) ) | ||
# encode CRC as LE | ||
r.extend([ (crc >> i) & 255 for i in range(0,32,8) ]) | ||
# payload, plaintext | ||
r.extend( s_bytes ) | ||
return r | ||
|
||
def bytes_to_ips( data, sequence ): | ||
r = [] | ||
if len(data) & 1: | ||
data.append(0) | ||
for i in range(0, len(data), 2): | ||
r.append( "226." + str(sequence) + "." + str(data[i+1]) + "." + str(data[i]) ) | ||
sequence += 1 | ||
return r | ||
|
||
multicast_head = bytes_to_ips([ ord(c) for c in "TYST01" ], 120) | ||
|
||
def encode_multicast_body( password, ssid, token_group ): | ||
r = [] | ||
ssid_encoded = encode_plain(ssid) | ||
r.extend( bytes_to_ips( ssid_encoded, 64 ) ) | ||
password_encoded = encode_pw(password) | ||
r.extend( bytes_to_ips( password_encoded, 0 ) ) | ||
token_group_encoded = encode_plain(token_group) | ||
r.extend( bytes_to_ips( token_group_encoded, 32 ) ) | ||
return r | ||
|
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
""" | ||
smartconfig.py | ||
Created by kueblc on 2019-01-25. | ||
Configure Tuya devices via smartconfig without the Tuya cloud or app | ||
broadcast strategy ported from https://github.com/tuyapi/link | ||
multicast strategy reverse engineered by kueblc | ||
""" | ||
|
||
# Defaults | ||
|
||
# time to sleep inbetween packets, 5ms | ||
GAP = 5 / 1000. | ||
|
||
BIND_ADDRESS = '10.42.42.1' | ||
|
||
MULTICAST_TTL = 1 | ||
|
||
from socket import * | ||
from time import sleep | ||
|
||
class SmartConfigSocket(object): | ||
def __init__( self, address = BIND_ADDRESS, gap = GAP ): | ||
self._socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) | ||
self._socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) | ||
self._socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) | ||
self._socket.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, MULTICAST_TTL) | ||
self._socket.bind((address, 0)) | ||
self._gap = gap | ||
|
||
def send_broadcast( self, data ): | ||
for length in data: | ||
self._socket.sendto( b'\0' * length, ('255.255.255.255', 30011)) | ||
sleep(self._gap) | ||
|
||
def send_multicast( self, data ): | ||
for ip in data: | ||
self._socket.sendto( b'\0', (ip, 30012)) | ||
sleep(self._gap) | ||
|
||
from broadcast import broadcast_head, encode_broadcast_body | ||
from multicast import multicast_head, encode_multicast_body | ||
|
||
def smartconfig( password, ssid, region, token, secret ): | ||
sock = SmartConfigSocket() | ||
token_group = region + token + secret | ||
broadcast_body = encode_broadcast_body( password, ssid, token_group ) | ||
# print(broadcast_body) | ||
multicast_body = encode_multicast_body( password, ssid, token_group ) | ||
# print(multicast_body) | ||
# print("sending header") | ||
for i in range(40): # originally 143, that's more than we really need | ||
sock.send_multicast(multicast_head) | ||
sock.send_broadcast(broadcast_head) | ||
for i in range(10): # originally 30, again, more than necessary | ||
# print("sending body iteration " + str(i)) | ||
print('.', end='', flush=True) # quick and dirty progress meter | ||
sock.send_multicast(multicast_head) | ||
sock.send_multicast(multicast_body) | ||
sock.send_broadcast(broadcast_body) | ||
|
This file contains 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