tplinkrouterc6u 5.5.0__py3-none-any.whl → 5.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,455 @@
1
+ from unittest import main, TestCase
2
+ from ipaddress import IPv4Address
3
+ from macaddress import EUI48
4
+ from tplinkrouterc6u.common.dataclass import Firmware, Status, Device
5
+ from tplinkrouterc6u.common.dataclass import IPv4Status, IPv4Reservation, IPv4DHCPLease, VPNStatus
6
+ from tplinkrouterc6u import Connection, ClientException
7
+ from tplinkrouterc6u.client.c80 import TplinkC80Router
8
+
9
+
10
+ IPV4_STATUS_RESPONSE = ('00000\r\nid 1|1,0,0\r\nauthKey token\r\nreserved\r\nsetWzd 8\r\nmode 1\r\nlogLevel 3\r\n'
11
+ 'fastpath 1\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01\r\nwanMacType 0\r\n'
12
+ 'modelMergeCursor 8\r\nid 4|1,0,0\r\nip 192.168.0.1\r\nmask 255.255.255.0\r\nmode 0\r\n'
13
+ 'smartIp 1\r\ngateway 192.168.0.1\r\nid 8|1,0,0\r\nenable 1\r\npoolStart 192.168.0.100\r\n'
14
+ 'poolEnd 192.168.0.249\r\nleaseTime 120\r\ndns 0 0.0.0.0\r\ndns 1 0.0.0.0\r\ngateway 0.0.0.0'
15
+ '\r\nhostName\r\nrelayEnableAll 0\r\nrelayEnable 0\r\nrelayServer 0.0.0.0\r\nid 22|1,0,0'
16
+ '\r\nenable 1\r\nneedPnpDetect 0\r\nocnDetect 0\r\nreserved\r\nlinkMode 0\r\nlinkType 0\r\n'
17
+ 'id 23|1,0,0\r\nip 1.0.1.1\r\nmask 255.255.252.0\r\ngateway 1.0.0.1\r\ndns 0 8.8.8.8\r\n'
18
+ 'dns 1 8.8.8.8\r\nstatus 1\r\ncode 0\r\nupTime 42601\r\ninPkts 12018031\r\ninOctets 1549640652'
19
+ '\r\noutPkts 31192286\r\noutOctets 182616925\r\ninRates 939\r\noutRates 318\r\ndualMode 0\r\n'
20
+ 'dualIp 0.0.0.0\r\ndualMask 0.0.0.0\r\ndualGateway 0.0.0.0\r\ndualDns 0 0.0.0.0\r\n'
21
+ 'dualDns 1 0.0.0.0\r\ndualCode 1\r\ndualStatus 3\r\ninternetDnsDetect 1\r\nid 24|1,0,0\r\n'
22
+ 'ip 1.0.0.1\r\nmask 255.255.255.0\r\ngateway 1.0.0.1\r\ndns 0 8.8.8.8\r\ndns 1 8.8.8.8\r\n'
23
+ 'mtu 1500')
24
+
25
+
26
+ IPV4_RESPONSE_TEXT = ('00000\r\nid 1|1,0,0\r\nauthKey token\r\nreserved\r\nsetWzd 8\r\nmode 1\r\nlogLevel 3'
27
+ '\r\nfastpath 1\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01\r\nwanMacType 0'
28
+ '\r\nmodelMergeCursor 8\r\nid 4|1,0,0\r\nip 192.168.0.1\r\nmask 255.255.255.0\r\n'
29
+ 'mode 0\r\nsmartIp 1\r\ngateway 192.168.0.1\r\nid 8|1,0,0\r\nenable 1\r\n'
30
+ 'poolStart 192.168.0.100\r\npoolEnd 192.168.0.249\r\nleaseTime 120\r\ndns 0 0.0.0.0'
31
+ '\r\ndns 1 0.0.0.0\r\ngateway 0.0.0.0\r\nhostName\r\nrelayEnableAll 0\r\n'
32
+ 'relayEnable 0\r\nrelayServer 0.0.0.0\r\nid 22|1,0,0\r\nenable 1\r\nneedPnpDetect 0'
33
+ '\r\nocnDetect 0\r\nreserved\r\nlinkMode 0\r\nlinkType 0\r\nid 23|1,0,0\r\n'
34
+ 'ip 1.1.1.1\r\nmask 255.255.252.0\r\ngateway 1.1.1.2\r\ndns 0 5.8.8.8\r\n'
35
+ 'dns 1 5.8.8.8\r\nstatus 1\r\ncode 0\r\nupTime 15000\r\ninPkts 9537954\r\n'
36
+ 'inOctets 327534332\r\noutPkts 24449491\r\noutOctets 2189487468\r\ninRates 42346'
37
+ '\r\noutRates 19222\r\ndualMode 0\r\ndualIp 0.0.0.0\r\ndualMask 0.0.0.0\r\n'
38
+ 'dualGateway 0.0.0.0\r\ndualDns 0 0.0.0.0\r\ndualDns 1 0.0.0.0\r\ndualCode 1'
39
+ '\r\ndualStatus 3\r\ninternetDnsDetect 1\r\nid 24|1,0,0\r\nip 1.0.0.1\r\n'
40
+ 'mask 255.255.255.0\r\ngateway 1.0.0.1\r\ndns 0 8.8.8.8\r\ndns 1 8.8.8.8\r\nmtu 1500')
41
+
42
+ STATUS_RESPONSE_TEXT = ('00000\r\nid 1|1,0,0\r\nauthKey aaa3eKKee\r\nreserved\r\nsetWzd 8\r\nmode 1\r\n'
43
+ 'logLevel 3\r\nfastpath 1\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01'
44
+ '\r\nwanMacType 0\r\nmodelMergeCursor 8\r\nid 4|1,0,0\r\nip 192.168.0.1\r\n'
45
+ 'mask 255.255.255.0\r\nmode 0\r\nsmartIp 1\r\ngateway 192.168.0.1\r\nid 23|1,0,0'
46
+ '\r\nip 100.100.100.100\r\nmask 255.255.252.0\r\ngateway 100.100.105.1\r\n'
47
+ 'dns 0 100.100.0.1\r\ndns 1 100.100.0.1\r\nstatus 1\r\ncode 0\r\nupTime 30814980'
48
+ '\r\ninPkts 3014881\r\ninOctets 1502000045\r\noutPkts 8216676\r\noutOctets 2500478193'
49
+ '\r\ninRates 337\r\noutRates 714\r\ndualMode 0\r\ndualIp 0.0.0.0\r\ndualMask 0.0.0.0'
50
+ '\r\ndualGateway 0.0.0.0\r\ndualDns 0 0.0.0.0\r\ndualDns 1 0.0.0.0\r\ndualCode 0'
51
+ '\r\ndualStatus 0\r\ninternetDnsDetect 1\r\nid 13|1,0,0\r\nip 0 192.168.0.1\r\n'
52
+ 'ip 1 192.168.0.2\r\nip 2 192.168.0.3\r\nip 3 192.168.0.4\r\nip 4 192.168.0.5'
53
+ '\r\nip 5 192.168.0.6\r\nmac 0 00-00-00-00-00-02\r\nmac 1 00-00-00-00-00-03'
54
+ '\r\nmac 2 00-00-00-00-00-04\r\nmac 3 00-00-00-00-00-05\r\nmac 4 00-00-00-00-00-06'
55
+ '\r\nmac 5 00-00-00-00-00-07\r\nbindEntry 0 0\r\nbindEntry 1 0\r\nbindEntry 2 0'
56
+ '\r\nbindEntry 3 0\r\nbindEntry 4 0\r\nbindEntry 5 0\r\nstaMgtEntry 0 0\r\n'
57
+ 'staMgtEntry 1 0\r\nstaMgtEntry 2 0\r\nstaMgtEntry 3 0\r\nstaMgtEntry 4 1'
58
+ '\r\nstaMgtEntry 5 0\r\ntype 0 3\r\ntype 1 1\r\ntype 2 2\r\ntype 3 1\r\ntype 4 13'
59
+ '\r\ntype 5 0\r\nonline 0 0\r\nonline 1 0\r\nonline 2 1\r\nonline 3 1\r\nonline 4 1'
60
+ '\r\nonline 5 1\r\nblocked 0 0\r\nblocked 1 0\r\nblocked 2 0\r\nblocked 3 0\r\n'
61
+ 'blocked 4 0\r\nblocked 5 0\r\nqosPrior 0 0\r\nqosPrior 1 0\r\nqosPrior 2 0\r\n'
62
+ 'qosPrior 3 0\r\nqosPrior 4 0\r\nqosPrior 5 0\r\nup 0 0\r\nup 1 0\r\nup 2 30\r\n'
63
+ 'up 3 800\r\nup 4 1824\r\nup 5 600\r\ndown 0 0\r\ndown 1 0\r\ndown 2 200\r\n'
64
+ 'down 3 400\r\ndown 4 800\r\ndown 5 50\r\nupLimit 0 204800\r\nupLimit 1 204800\r\n'
65
+ 'upLimit 2 204800\r\nupLimit 3 204800\r\nupLimit 4 204800\r\nupLimit 5 204800\r\n'
66
+ 'downLimit 0 1048576\r\ndownLimit 1 1048576\r\ndownLimit 2 1048576\r\n'
67
+ 'downLimit 3 1048576\r\ndownLimit 4 1048576\r\ndownLimit 5 1048576\r\nname 0 Laptop'
68
+ '\r\nname 1 iPhone\r\nname 2 Laptop2\r\nname 3 iPhone2\r\nname 4 IoT_thing\r\n'
69
+ 'name 5 PC\r\nuBandwidth 0 0\r\nuBandwidth 1 0\r\nuBandwidth 2 0\r\nuBandwidth 3 0'
70
+ '\r\nuBandwidth 4 0\r\nuBandwidth 5 0\r\nuStandard 0 0\r\nuStandard 1 0\r\n'
71
+ 'uStandard 2 0\r\nuStandard 3 2\r\nuStandard 4 2\r\nuStandard 5 0\r\ndevType 0 0'
72
+ '\r\ndevType 1 0\r\ndevType 2 0\r\ndevType 3 0\r\ndevType 4 0\r\ndevType 5 0\r\n'
73
+ 'priTime 0 0\r\npriTime 1 0\r\npriTime 2 0\r\npriTime 3 0\r\npriTime 4 0\r\n'
74
+ 'priTime 5 0\r\nleaseTime 0 0\r\nleaseTime 1 0\r\nleaseTime 2 0\r\nleaseTime 3 0'
75
+ '\r\nleaseTime 4 0\r\nleaseTime 5 0\r\ntotalVal 0 450\r\ntotalVal 1 5\r\n'
76
+ 'totalVal 2 13\r\ntotalVal 3 956\r\ntotalVal 4 13\r\ntotalVal 5 53\r\ntotalUnit 0 2'
77
+ '\r\ntotalUnit 1 2\r\ntotalUnit 2 2\r\ntotalUnit 3 2\r\ntotalUnit 4 2\r\n'
78
+ 'totalUnit 5 2\r\ndhcpsEntry 0 0\r\ndhcpsEntry 1 0\r\ndhcpsEntry 2 0\r\n'
79
+ 'dhcpsEntry 3 0\r\ndhcpsEntry 4 0\r\ndhcpsEntry 5 0\r\nduration 0 0\r\n'
80
+ 'duration 1 0\r\nduration 2 0\r\nduration 3 73\r\nduration 4 5388\r\n'
81
+ 'duration 5 8559\r\ntxRate 0 0\r\ntxRate 1 0\r\ntxRate 2 0\r\ntxRate 3 156\r\n'
82
+ 'txRate 4 130\r\ntxRate 5 0\r\nrxRate 0 0\r\nrxRate 1 0\r\nrxRate 2 0\r\n'
83
+ 'rxRate 3 130\r\nrxRate 4 173\r\nrxRate 5 0\r\naveRssi 0 0\r\naveRssi 1 0\r\n'
84
+ 'aveRssi 2 0\r\naveRssi 3 33\r\naveRssi 4 46\r\naveRssi 5 0\r\nslEnable 0 0\r\n'
85
+ 'slEnable 1 0\r\nslEnable 2 0\r\nslEnable 3 0\r\nslEnable 4 0\r\nslEnable 5 0'
86
+ '\r\npriScheStatus 0 0\r\npriScheStatus 1 0\r\npriScheStatus 2 0\r\n'
87
+ 'priScheStatus 3 0\r\npriScheStatus 4 0\r\npriScheStatus 5 0\r\nstart 0 0\r\n'
88
+ 'start 1 0\r\nstart 2 0\r\nstart 3 0\r\nstart 4 0\r\nstart 5 0\r\nend 0 0\r\n'
89
+ 'end 1 0\r\nend 2 0\r\nend 3 0\r\nend 4 0\r\nend 5 0\r\nday 0 0\r\nday 1 0\r\n'
90
+ 'day 2 0\r\nday 3 0\r\nday 4 0\r\nday 5 0\r\nstartMin 0 0\r\nstartMin 1 0\r\n'
91
+ 'startMin 2 0\r\nstartMin 3 0\r\nstartMin 4 0\r\nstartMin 5 0\r\nendMin 0 0\r\n'
92
+ 'endMin 1 0\r\nendMin 2 0\r\nendMin 3 0\r\nendMin 4 0\r\nendMin 5 0\r\nrate 0 0'
93
+ '\r\nrate 1 0\r\nrate 2 0\r\nrate 3 156\r\nrate 4 173\r\nrate 5 0\r\ntxPkt 0 0'
94
+ '\r\ntxPkt 1 0\r\ntxPkt 2 0\r\ntxPkt 3 42602\r\ntxPkt 4 119683\r\ntxPkt 5 0'
95
+ '\r\nrxPkt 0 0\r\nrxPkt 1 0\r\nrxPkt 2 0\r\nrxPkt 3 6343\r\nrxPkt 4 275076\r\n'
96
+ 'rxPkt 5 0\r\nid 33|1,1,0\r\nuUnit 0\r\ncSsidPrefix\r\nuRadiusIp 0.0.0.0\r\n'
97
+ 'uRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
98
+ '\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
99
+ 'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\n'
100
+ 'bBcastSsid 1\r\ncSsid TP-Link\r\nbSecurityEnable 1\r\nuAuthType 3\r\nuWEPSecOpt 3'
101
+ '\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3'
102
+ '\r\ncRadiusSecret\r\ncPskSecret admin\r\nbSecCheck 0\r\nbEnabled 1\r\n'
103
+ 'cUsrPIN 11100111\r\nbConfigured 1\r\nbIsLocked 0\r\nbEnRtPIN 1\r\n'
104
+ 'bWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1'
105
+ '\r\nSecurityType 2\r\nbApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1'
106
+ '\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 1\r\nbTr069APEnable 0'
107
+ '\r\nbTr069SSIDEnable 0\r\nid 33|2,1,0\r\nuUnit 0\r\ncSsidPrefix\r\nuRadiusIp 0.0.0.0'
108
+ '\r\nuRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
109
+ '\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
110
+ 'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\n'
111
+ 'bBcastSsid 1\r\ncSsid TP-Link\r\nbSecurityEnable 1\r\nuAuthType 3\r\nuWEPSecOpt 3'
112
+ '\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3'
113
+ '\r\ncRadiusSecret\r\ncPskSecret admin\r\nbSecCheck 0\r\nbEnabled 1\r\n'
114
+ 'cUsrPIN 11100111\r\nbConfigured 1\r\nbIsLocked 0\r\nbEnRtPIN 1\r\nbWifiBtnRecEnable 1'
115
+ '\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1\r\nSecurityType 2\r\n'
116
+ 'bApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1\r\nuMaxDownloadSpeed -1\r\n'
117
+ 'bwCtrlEnable 0\r\nenableBackup 1\r\nbTr069APEnable 0\r\nbTr069SSIDEnable 0\r\n'
118
+ 'id 33|1,2,0\r\nuUnit 1\r\ncSsidPrefix Guest\r\nuRadiusIp 0.0.0.0\r\n'
119
+ 'uRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
120
+ '\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
121
+ 'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 0\r\n'
122
+ 'bBcastSsid 1\r\ncSsid TP-Link_Guest\r\nbSecurityEnable 0\r\nuAuthType 3\r\n'
123
+ 'uWEPSecOpt 3\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 3\r\nuRadiusEncryptType 1\r\n'
124
+ 'uPSKEncryptType 3\r\ncRadiusSecret\r\ncPskSecret\r\nbSecCheck 0\r\nbEnabled 1'
125
+ '\r\ncUsrPIN 11100111\r\nbConfigured 0\r\nbIsLocked 0\r\nbEnRtPIN 0\r\n'
126
+ 'bWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 0'
127
+ '\r\nSecurityType 1\r\nbApIsolated 1\r\neffectiveTime 0\r\nuMaxUploadSpeed -1'
128
+ '\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 0\r\nbTr069APEnable 0'
129
+ '\r\nbTr069SSIDEnable 0\r\nid 33|2,2,0\r\nuUnit 1\r\ncSsidPrefix Guest\r\n'
130
+ 'uRadiusIp 0.0.0.0\r\nuRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\n'
131
+ 'uKeyLength 0 0\r\nuKeyLength 1 0\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\n'
132
+ 'cKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\ncKeyVal 3\r\nuRadiusPort 1812\r\n'
133
+ 'uKeyType 0\r\nuDefaultKey 1\r\nbEnable 0\r\nbBcastSsid 1\r\ncSsid TP-Link_Guest_5G'
134
+ '\r\nbSecurityEnable 0\r\nuAuthType 3\r\nuWEPSecOpt 3\r\nuRadiusSecOpt 3\r\n'
135
+ 'uPSKSecOpt 3\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3\r\ncRadiusSecret\r\n'
136
+ 'cPskSecret\r\nbSecCheck 0\r\nbEnabled 1\r\ncUsrPIN 11100111\r\nbConfigured 0\r\n'
137
+ 'bIsLocked 0\r\nbEnRtPIN 0\r\nbWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\n'
138
+ 'bMwdsEnable 0\r\nbLanAccess 0\r\nSecurityType 1\r\nbApIsolated 1\r\neffectiveTime 0'
139
+ '\r\nuMaxUploadSpeed -1\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 0'
140
+ '\r\nbTr069APEnable 0\r\nbTr069SSIDEnable 0\r\nid 33|1,9,0\r\nuUnit 9\r\n'
141
+ 'cSsidPrefix IoT\r\nuRadiusIp 0.0.0.0\r\nuRadiusGKUpdateIntvl 0\r\n'
142
+ 'uPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0\r\nuKeyLength 2 0\r\n'
143
+ 'uKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\ncKeyVal 3\r\n'
144
+ 'uRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\nbBcastSsid 0'
145
+ '\r\ncSsid TP-Link_IoT\r\nbSecurityEnable 1\r\nuAuthType 3\r\nuWEPSecOpt 3\r\n'
146
+ 'uRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3'
147
+ '\r\ncRadiusSecret\r\ncPskSecret 11100111\r\nbSecCheck 0\r\nbEnabled 1\r\n'
148
+ 'cUsrPIN 11100111\r\nbConfigured 0\r\nbIsLocked 0\r\nbEnRtPIN 0\r\nbWifiBtnRecEnable 1'
149
+ '\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1\r\nSecurityType 2\r\n'
150
+ 'bApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1\r\nuMaxDownloadSpeed -1'
151
+ '\r\nbwCtrlEnable 0\r\nenableBackup 0\r\nbTr069APEnable 0\r\nbTr069SSIDEnable 0'
152
+ '\r\nid 33|2,9,0\r\nuUnit 9\r\ncSsidPrefix IoT\r\nuRadiusIp 0.0.0.0\r\n'
153
+ 'uRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
154
+ '\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
155
+ 'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\n'
156
+ 'bBcastSsid 0\r\ncSsid TP-Link_IoT\r\nbSecurityEnable 1\r\nuAuthType 3\r\n'
157
+ 'uWEPSecOpt 3\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\n'
158
+ 'uPSKEncryptType 3\r\ncRadiusSecret\r\ncPskSecret 11100111\r\nbSecCheck 0\r\n'
159
+ 'bEnabled 1\r\ncUsrPIN 11100111\r\nbConfigured 0\r\nbIsLocked 0\r\nbEnRtPIN 0'
160
+ '\r\nbWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1'
161
+ '\r\nSecurityType 2\r\nbApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1'
162
+ '\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 0\r\nbTr069APEnable 0'
163
+ '\r\nbTr069SSIDEnable 0')
164
+
165
+
166
+ class ResponseMock():
167
+ def __init__(self, text, status_code=0):
168
+ self.text = text
169
+ self.status_code = status_code
170
+
171
+
172
+ class TplinkC80RouterTest(TplinkC80Router):
173
+ response = ''
174
+
175
+ def request(self, code: int, asyn: int, use_token: bool = False, data: str = None) -> dict | None:
176
+
177
+ # Responses
178
+ if code == 2 and asyn == 1:
179
+ if use_token is False:
180
+ if data == '0|1,0,0':
181
+ # Supports
182
+ return ResponseMock(self.response, 200)
183
+ else:
184
+ # Authorization
185
+ return ResponseMock('blabla\r\nblabla\r\nblabla\r\nauthinfo1\r\nauthinfo2')
186
+ elif use_token is True:
187
+ return ResponseMock(self.response)
188
+ elif (code == 16 or code == 7) and asyn == 0:
189
+ if use_token is False:
190
+ # Authorization
191
+ return ResponseMock('00000\r\n010001\r\nBC97577E65233B3E1137C61091D64176C334E52AD78FFBDDABC826B685435E'
192
+ '9D3DE83FE70C2AC62D6B13BD8EADA10B5623F9354DA0E99636A4F5519CA2DC2DC3\r\n12345656')
193
+ elif use_token is True:
194
+ # Authorization
195
+ return ResponseMock('00000')
196
+
197
+ raise ClientException()
198
+
199
+ def set_encrypted_response(self, response_text) -> None:
200
+ self.response = self._encrypt_body(response_text).split('data=')[1]
201
+
202
+
203
+ class TestTPLinkClient(TestCase):
204
+
205
+ def test_supports(self) -> None:
206
+ response = ('00000\r\nid 0|1,0,0\r\nfullName AC1900%20MU-MIMO%20Wi-Fi%20Router\r\nfacturer TP-Link\r\nmodelName'
207
+ ' Archer%20C80\r\nmodelVer 2.20\r\nsoftVer 1.13.15%20Build%20240812%20Rel.53972n(4555)\r\nhardVer'
208
+ ' Archer%20C80%202.20\r\nspecialId 0x5545\r\ncountryCode 0x455a\r\nmainVer 0x5a010d0f\r\nminorVer'
209
+ ' 0x1\r\nfacturerType 0')
210
+
211
+ client = TplinkC80RouterTest('', '')
212
+ client.response = response
213
+ supports = client.supports()
214
+ self.assertTrue(supports)
215
+
216
+ def test_authorize(self) -> None:
217
+ client = TplinkC80RouterTest('', '')
218
+ client.authorize()
219
+
220
+ encryption = client._encryption
221
+ self.assertEqual(encryption.ee_rsa, '010001')
222
+ self.assertEqual(encryption.nn_rsa, 'BC97577E65233B3E1137C61091D64176C334E52AD78FFBDDABC826B685435E9D3DE83FE70C'
223
+ '2AC62D6B13BD8EADA10B5623F9354DA0E99636A4F5519CA2DC2DC3')
224
+ self.assertEqual(encryption.seq, '12345656')
225
+
226
+ def test_get_firmware(self) -> None:
227
+ response = ('00000\r\nid 0|1,0,0\r\nfullName AC1900%20MU-MIMO%20Wi-Fi%20Router\r\nfacturer TP-Link\r\nmodelName'
228
+ ' Archer%20C80\r\nmodelVer 2.20\r\nsoftVer 1.13.15%20Build%20240812%20Rel.53972n(4555)\r\nhardVer'
229
+ ' Archer%20C80%202.20\r\nspecialId 0x5545\r\ncountryCode 0x455a\r\nmainVer 0x5a010d0f\r\nminorVer'
230
+ ' 0x1\r\nfacturerType 0')
231
+
232
+ client = TplinkC80RouterTest('', '')
233
+ client.authorize()
234
+
235
+ client.set_encrypted_response(response)
236
+
237
+ firmware = client.get_firmware()
238
+
239
+ self.assertIsInstance(firmware, Firmware)
240
+ self.assertEqual(firmware.hardware_version, 'Archer C80 2.20')
241
+ self.assertEqual(firmware.model, 'Archer C80')
242
+ self.assertEqual(firmware.firmware_version, '1.13.15 Build 240812 Rel.53972n(4555)')
243
+
244
+ def test_get_ipv4_status(self) -> None:
245
+
246
+ client = TplinkC80RouterTest('', '')
247
+ client.authorize()
248
+
249
+ client.set_encrypted_response(IPV4_STATUS_RESPONSE)
250
+
251
+ ipv4_status: IPv4Status = client.get_ipv4_status()
252
+
253
+ self.assertIsInstance(ipv4_status, IPv4Status)
254
+ self.assertEqual(ipv4_status.wan_macaddress, EUI48('00-00-00-00-00-01'))
255
+ self.assertEqual(ipv4_status._wan_ipv4_ipaddr, IPv4Address('1.0.1.1'))
256
+ self.assertEqual(ipv4_status._wan_ipv4_gateway, IPv4Address('1.0.0.1'))
257
+ self.assertEqual(ipv4_status._wan_ipv4_conntype, 'Dynamic IP')
258
+ self.assertEqual(ipv4_status._wan_ipv4_netmask, IPv4Address('255.255.252.0'))
259
+ self.assertEqual(ipv4_status._wan_ipv4_pridns, IPv4Address('8.8.8.8'))
260
+ self.assertEqual(ipv4_status._wan_ipv4_snddns, IPv4Address('8.8.8.8'))
261
+ self.assertEqual(ipv4_status._lan_macaddr, EUI48('00-00-00-00-00-00'))
262
+ self.assertEqual(ipv4_status._lan_ipv4_ipaddr, IPv4Address('192.168.0.1'))
263
+ self.assertEqual(ipv4_status.lan_ipv4_dhcp_enable, True)
264
+ self.assertEqual(ipv4_status._lan_ipv4_netmask, IPv4Address('255.255.255.0'))
265
+
266
+ def test_get_ipv4_reservations(self) -> None:
267
+ response = ('00000\r\nid 12|1,0,0\r\nip 0 192.168.0.112\r\nip 1 0.0.0.0\r\nmac 0 00-00-00-00-00-00\r\n'
268
+ 'mac 1 00-00-00-00-00-01\r\nreserved 0\r\nreserved 1\r\nbindEntry 0 0\r\nbindEntry 1 0\r\n'
269
+ 'staMgtEntry 0 0\r\nstaMgtEntry 1 1\r\nname 0 Galaxy-S21\r\nname 1 Camera\r\nreserved_name 0\r\n'
270
+ 'reserved_name 1\r\nblocked 0 0\r\nblocked 1 0\r\nupLimit 0 0\r\nupLimit 1 0\r\ndownLimit 0 0'
271
+ '\r\ndownLimit 1 0\r\nqosEntry 0 0\r\nqosEntry 1 0\r\npriTime 0 0\r\npriTime 1 0\r\n'
272
+ 'dhcpsEntry 0 1\r\ndhcpsEntry 1 0\r\ndhcpsEnable 0 1\r\ndhcpsEnable 1 0\r\nslEnable 0 0\r\n'
273
+ 'slEnable 1 0\r\nstart 0 0\r\nstart 1 0\r\nend 0 0\r\nend 1 0\r\nday 0 0\r\nday 1 0\r\n'
274
+ 'startMin 0 0\r\nstartMin 1 0\r\nendMin 0 0\r\nendMin 1 0\r\ndevType 0 0\r\ndevType 1 0\r\n'
275
+ 'reserved2 0 0\r\nreserved2 1 0\r\ndisable 1')
276
+
277
+ client = TplinkC80RouterTest('', '')
278
+ client.authorize()
279
+
280
+ client.set_encrypted_response(response)
281
+
282
+ ipv4_reservations: list[IPv4Reservation] = client.get_ipv4_reservations()
283
+ ipv4_reservation: IPv4Reservation = ipv4_reservations[0]
284
+
285
+ self.assertIsInstance(ipv4_reservation, IPv4Reservation)
286
+ self.assertEqual(ipv4_reservation.macaddress, EUI48('00-00-00-00-00-00'))
287
+ self.assertEqual(ipv4_reservation.ipaddress, IPv4Address('192.168.0.112'))
288
+ self.assertEqual(ipv4_reservation.hostname, 'Galaxy-S21')
289
+ self.assertEqual(ipv4_reservation.enabled, True)
290
+
291
+ def test_get_dhcp_leases(self) -> None:
292
+ response = ('00000\r\nid 9|1,0,0\r\nhostName 0 Galaxy-S21\r\nhostName 1 iPhone\r\nhostName 2 PC\r\n'
293
+ 'hostName 3 Laptop\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01\r\n'
294
+ 'mac 2 00-00-00-00-00-02\r\nmac 3 00-00-00-00-00-03\r\nreserved 0\r\nreserved 1'
295
+ '\r\nreserved 2\r\nreserved 3\r\nstate 0 5\r\nstate 1 5\r\nstate 2 5\r\nstate 3 5'
296
+ '\r\nip 0 192.168.0.112\r\nip 1 192.168.0.101\r\nip 2 192.168.0.245\r\nip 3 192.168.0.186'
297
+ '\r\nexpires 0 4294967295\r\nexpires 1 3669\r\nexpires 2 4025\r\nexpires 3 4202')
298
+
299
+ client = TplinkC80RouterTest('', '')
300
+ client.authorize()
301
+
302
+ client.set_encrypted_response(response)
303
+
304
+ dhcp_leases: list[IPv4DHCPLease] = client.get_dhcp_leases()
305
+
306
+ self.assertIsInstance(dhcp_leases[0], IPv4DHCPLease)
307
+ self.assertEqual(dhcp_leases[0].macaddress, EUI48('00-00-00-00-00-00'))
308
+ self.assertEqual(dhcp_leases[0].ipaddress, IPv4Address('192.168.0.112'))
309
+ self.assertEqual(dhcp_leases[0].hostname, 'Galaxy-S21')
310
+ self.assertEqual(dhcp_leases[0].lease_time, 'expires 4294967295')
311
+
312
+ self.assertIsInstance(dhcp_leases[1], IPv4DHCPLease)
313
+ self.assertEqual(dhcp_leases[1].macaddress, EUI48('00-00-00-00-00-01'))
314
+ self.assertEqual(dhcp_leases[1].ipaddress, IPv4Address('192.168.0.101'))
315
+ self.assertEqual(dhcp_leases[1].hostname, 'iPhone')
316
+ self.assertEqual(dhcp_leases[1].lease_time, 'expires 3669')
317
+
318
+ def test_get_vpn_status(self) -> None:
319
+ response = '00000\r\nid 22|1,0,0\r\nenable 1\r\nneedPnpDetect 0\r\nocnDetect 0\r\n' \
320
+ 'reserved\r\nlinkMode 0\r\nlinkType 4'
321
+
322
+ client = TplinkC80RouterTest('', '')
323
+ client.authorize()
324
+
325
+ client.set_encrypted_response(response)
326
+
327
+ vpn_status: VPNStatus = client.get_vpn_status()
328
+
329
+ self.assertIsInstance(vpn_status, VPNStatus)
330
+ self.assertEqual(vpn_status.openvpn_clients_total, 0)
331
+ self.assertEqual(vpn_status.pptpvpn_clients_total, 0)
332
+ self.assertEqual(vpn_status.pptpvpn_enable, True)
333
+
334
+ def test_get_status(self) -> None:
335
+ client = TplinkC80RouterTest('', '')
336
+ client.authorize()
337
+
338
+ client.set_encrypted_response(STATUS_RESPONSE_TEXT)
339
+ status = client.get_status()
340
+
341
+ self.assertIsInstance(status, Status)
342
+ self.assertEqual(status.wan_macaddr, '00-00-00-00-00-01')
343
+ self.assertIsInstance(status.wan_macaddress, EUI48)
344
+ self.assertEqual(status.lan_macaddr, '00-00-00-00-00-00')
345
+ self.assertIsInstance(status.lan_macaddress, EUI48)
346
+ self.assertEqual(status.wan_ipv4_addr, '100.100.100.100')
347
+ self.assertIsInstance(status.lan_ipv4_address, IPv4Address)
348
+ self.assertEqual(status.lan_ipv4_addr, '192.168.0.1')
349
+ self.assertEqual(status.wan_ipv4_gateway, '100.100.105.1')
350
+ self.assertIsInstance(status.wan_ipv4_address, IPv4Address)
351
+ self.assertEqual(status.wired_total, 1)
352
+ self.assertEqual(status.wifi_clients_total, 1)
353
+ self.assertEqual(status.guest_clients_total, 1)
354
+ self.assertEqual(status.clients_total, 4)
355
+ self.assertEqual(status.iot_clients_total, 1)
356
+ self.assertFalse(status.guest_2g_enable)
357
+ self.assertFalse(status.guest_5g_enable)
358
+ self.assertTrue(status.iot_2g_enable)
359
+ self.assertTrue(status.iot_5g_enable)
360
+ self.assertTrue(status.wifi_2g_enable)
361
+ self.assertTrue(status.wifi_5g_enable)
362
+ self.assertEqual(status.wan_ipv4_uptime, 308149)
363
+ self.assertEqual(status.mem_usage, None)
364
+ self.assertEqual(status.cpu_usage, None)
365
+ self.assertEqual(len(status.devices), 6)
366
+
367
+ device = status.devices[0]
368
+ self.assertIsInstance(device, Device)
369
+ self.assertEqual(device.type, Connection.UNKNOWN)
370
+ self.assertEqual(device.macaddr, '00-00-00-00-00-02')
371
+ self.assertIsInstance(device.macaddress, EUI48)
372
+ self.assertEqual(device.ipaddr, '192.168.0.1')
373
+ self.assertIsInstance(device.ipaddress, IPv4Address)
374
+ self.assertEqual(device.hostname, 'Laptop')
375
+ self.assertEqual(device.up_speed, 0)
376
+ self.assertEqual(device.down_speed, 0)
377
+
378
+ device = status.devices[1]
379
+ self.assertIsInstance(device, Device)
380
+ self.assertEqual(device.type, Connection.UNKNOWN)
381
+ self.assertEqual(device.macaddr, '00-00-00-00-00-03')
382
+ self.assertIsInstance(device.macaddress, EUI48)
383
+ self.assertEqual(device.ipaddr, '192.168.0.2')
384
+ self.assertIsInstance(device.ipaddress, IPv4Address)
385
+ self.assertEqual(device.hostname, 'iPhone')
386
+ self.assertEqual(device.up_speed, 0)
387
+ self.assertEqual(device.down_speed, 0)
388
+
389
+ device = status.devices[2]
390
+ self.assertIsInstance(device, Device)
391
+ self.assertEqual(device.type, Connection.GUEST_2G)
392
+ self.assertEqual(device.macaddr, '00-00-00-00-00-04')
393
+ self.assertIsInstance(device.macaddress, EUI48)
394
+ self.assertEqual(device.ipaddr, '192.168.0.3')
395
+ self.assertIsInstance(device.ipaddress, IPv4Address)
396
+ self.assertEqual(device.hostname, 'Laptop2')
397
+ self.assertEqual(device.up_speed, 30)
398
+ self.assertEqual(device.down_speed, 200)
399
+
400
+ device = status.devices[3]
401
+ self.assertIsInstance(device, Device)
402
+ self.assertEqual(device.type, Connection.HOST_2G)
403
+ self.assertEqual(device.macaddr, '00-00-00-00-00-05')
404
+ self.assertIsInstance(device.macaddress, EUI48)
405
+ self.assertEqual(device.ipaddr, '192.168.0.4')
406
+ self.assertIsInstance(device.ipaddress, IPv4Address)
407
+ self.assertEqual(device.hostname, 'iPhone2')
408
+ self.assertEqual(device.up_speed, 800)
409
+ self.assertEqual(device.down_speed, 400)
410
+
411
+ device = status.devices[4]
412
+ self.assertIsInstance(device, Device)
413
+ self.assertEqual(device.type, Connection.IOT_2G)
414
+ self.assertEqual(device.macaddr, '00-00-00-00-00-06')
415
+ self.assertIsInstance(device.macaddress, EUI48)
416
+ self.assertEqual(device.ipaddr, '192.168.0.5')
417
+ self.assertIsInstance(device.ipaddress, IPv4Address)
418
+ self.assertEqual(device.hostname, 'IoT_thing')
419
+ self.assertEqual(device.up_speed, 1824)
420
+ self.assertEqual(device.down_speed, 800)
421
+
422
+ device = status.devices[5]
423
+ self.assertIsInstance(device, Device)
424
+ self.assertEqual(device.type, Connection.WIRED)
425
+ self.assertEqual(device.macaddr, '00-00-00-00-00-07')
426
+ self.assertIsInstance(device.macaddress, EUI48)
427
+ self.assertEqual(device.ipaddr, '192.168.0.6')
428
+ self.assertIsInstance(device.ipaddress, IPv4Address)
429
+ self.assertEqual(device.hostname, 'PC')
430
+ self.assertEqual(device.up_speed, 600)
431
+ self.assertEqual(device.down_speed, 50)
432
+
433
+ def test_get_ipv4(self) -> None:
434
+ client = TplinkC80RouterTest('', '')
435
+ client.authorize()
436
+
437
+ client.set_encrypted_response(IPV4_RESPONSE_TEXT)
438
+ ipv4status: IPv4Status = client.get_ipv4_status()
439
+
440
+ self.assertIsInstance(ipv4status, IPv4Status)
441
+ self.assertEqual(ipv4status._wan_macaddr, EUI48('00-00-00-00-00-01'))
442
+ self.assertEqual(ipv4status._wan_ipv4_ipaddr, IPv4Address('1.1.1.1'))
443
+ self.assertEqual(ipv4status._wan_ipv4_gateway, IPv4Address('1.1.1.2'))
444
+ self.assertEqual(ipv4status._wan_ipv4_conntype, 'Dynamic IP')
445
+ self.assertEqual(ipv4status._wan_ipv4_netmask, IPv4Address('255.255.252.0'))
446
+ self.assertEqual(ipv4status._wan_ipv4_pridns, IPv4Address('5.8.8.8'))
447
+ self.assertEqual(ipv4status._wan_ipv4_snddns, IPv4Address('5.8.8.8'))
448
+ self.assertEqual(ipv4status._lan_macaddr, EUI48('00-00-00-00-00-00'))
449
+ self.assertEqual(ipv4status._lan_ipv4_ipaddr, IPv4Address('192.168.0.1'))
450
+ self.assertEqual(ipv4status.lan_ipv4_dhcp_enable, True)
451
+ self.assertEqual(ipv4status._lan_ipv4_netmask, IPv4Address('255.255.255.0'))
452
+
453
+
454
+ if __name__ == '__main__':
455
+ main()
@@ -4,7 +4,7 @@ from tplinkrouterc6u.client_abstract import AbstractRouter
4
4
  from tplinkrouterc6u.client.mr import TPLinkMRClient
5
5
  from tplinkrouterc6u.client.ex import TPLinkEXClient
6
6
  from tplinkrouterc6u.client.vr import TPLinkVRClient
7
- from tplinkrouterc6u.client.c6v4 import TplinkC6V4Router
7
+ from tplinkrouterc6u.client.c80 import TplinkC80Router
8
8
  from tplinkrouterc6u.client.c5400x import TplinkC5400XRouter
9
9
  from tplinkrouterc6u.client.c1200 import TplinkC1200Router
10
10
  from tplinkrouterc6u.client.xdr import TPLinkXDRClient
@@ -0,0 +1,406 @@
1
+ from dataclasses import dataclass
2
+ from logging import Logger
3
+ from urllib import parse
4
+ from base64 import b64encode, b64decode
5
+ from collections import defaultdict
6
+ from ipaddress import IPv4Address
7
+ import re
8
+ from Crypto.Cipher import AES
9
+ from Crypto.Util.Padding import pad, unpad
10
+ from macaddress import EUI48
11
+ import requests
12
+ from requests import Session
13
+ from tplinkrouterc6u.common.package_enum import Connection
14
+ from tplinkrouterc6u.common.exception import ClientException
15
+ from tplinkrouterc6u.common.encryption import EncryptionWrapper
16
+ from tplinkrouterc6u.common.dataclass import Firmware, Status, IPv4Status, IPv4Reservation
17
+ from tplinkrouterc6u.common.dataclass import IPv4DHCPLease, Device, VPNStatus
18
+ from tplinkrouterc6u.client_abstract import AbstractRouter
19
+
20
+
21
+ class RouterConstants:
22
+ AUTH_TOKEN_INDEX1 = 3
23
+ AUTH_TOKEN_INDEX2 = 4
24
+ DEFAULT_AES_VALUE = "0000000000000000"
25
+
26
+ HOST_WIFI_2G_REQUEST = '33|1,1,0'
27
+ HOST_WIFI_5G_REQUEST = '33|2,1,0'
28
+ GUEST_WIFI_2G_REQUEST = '33|1,2,0'
29
+ GUEST_WIFI_5G_REQUEST = '33|2,2,0'
30
+ IOT_WIFI_2G_REQUEST = '33|1,9,0'
31
+ IOT_WIFI_5G_REQUEST = '33|2,9,0'
32
+
33
+ CONNECTION_REQUESTS_MAP = {
34
+ Connection.HOST_2G: HOST_WIFI_2G_REQUEST,
35
+ Connection.HOST_5G: HOST_WIFI_5G_REQUEST,
36
+ Connection.GUEST_2G: GUEST_WIFI_2G_REQUEST,
37
+ Connection.GUEST_5G: GUEST_WIFI_5G_REQUEST,
38
+ Connection.IOT_2G: IOT_WIFI_2G_REQUEST,
39
+ Connection.IOT_5G: IOT_WIFI_5G_REQUEST
40
+ }
41
+
42
+ CONNECTION_TYPE_MAP = {
43
+ '0': "Dynamic IP",
44
+ '1': 'Static IP',
45
+ '2': 'PPPoE',
46
+ '3': 'L2TP',
47
+ '4': 'PPTP'
48
+ }
49
+
50
+
51
+ class RouterConfig:
52
+ """Configuration parameters for the router."""
53
+ ENCODING: str = ("yLwVl0zKqws7LgKPRQ84Mdt708T1qQ3Ha7xv3H7NyU84p21BriUWBU43odz3iP4rBL3cD02KZciXTysVXiV8"
54
+ "ngg6vL48rPJyAUw0HurW20xqxv9aYb4M9wK1Ae0wlro510qXeU07kV57fQMc8L6aLgMLwygtc0F10a0Dg70T"
55
+ "OoouyFhdysuRMO51yY5ZlOZZLEal1h0t9YQW0Ko7oBwmCAHoic4HYbUyVeU3sfQ1xtXcPcf1aT303wAQhv66qzW")
56
+ KEY: str = "RDpbLfCPsJZ7fiv"
57
+ PAD_CHAR: str = chr(187)
58
+
59
+
60
+ @dataclass
61
+ class EncryptionState:
62
+ """Holds encryption-related state."""
63
+ def __init__(self):
64
+ self.nn_rsa = ''
65
+ self.ee_rsa = ''
66
+ self.seq = ''
67
+ self.key_aes = ''
68
+ self.iv_aes = ''
69
+ self.aes_string = ''
70
+ self.token = ''
71
+
72
+
73
+ class TplinkC80Router(AbstractRouter):
74
+ DATA_REGEX = re.compile(r'id (\d+\|\d,\d,\d)\r\n(.*?)(?=\r\nid \d+\||$)', re.DOTALL)
75
+
76
+ def __init__(self, host: str, password: str, username: str = 'admin', logger: Logger = None,
77
+ verify_ssl: bool = True, timeout: int = 30) -> None:
78
+ super().__init__(host, password, username, logger, verify_ssl, timeout)
79
+ self._session = Session()
80
+ self._encryption = EncryptionState()
81
+
82
+ def supports(self) -> bool:
83
+ response = self.request(2, 1, data='0|1,0,0')
84
+ return response.status_code == 200 and response.text.startswith('00000')
85
+
86
+ def authorize(self) -> None:
87
+ encoded_password = TplinkC80Router._encrypt_password(self.password)
88
+
89
+ # Get token encryption strings and encrypt the password
90
+ response = self.request(2, 1)
91
+ self._encryption.token = TplinkC80Router._encode_token(encoded_password, response)
92
+
93
+ # Get RSA exponent, modulus and sequence number
94
+ response = self.request(16, 0, data='get')
95
+
96
+ responseText = response.text.splitlines()
97
+ if len(responseText) < 4:
98
+ raise ClientException("Invalid response for RSA keys from router")
99
+ self._encryption.ee_rsa = responseText[1]
100
+ self._encryption.nn_rsa = responseText[2]
101
+ self._encryption.seq = responseText[3]
102
+
103
+ # Generate key and initialization vector
104
+ self._encryption.key_aes = RouterConstants.DEFAULT_AES_VALUE
105
+ self._encryption.iv_aes = RouterConstants.DEFAULT_AES_VALUE
106
+ self._encryption.aes_string = f'k={self._encryption.key_aes}&i={self._encryption.iv_aes}'
107
+
108
+ # Encrypt AES string
109
+ aes_string_encrypted = EncryptionWrapper.rsa_encrypt(self._encryption.aes_string, self._encryption.nn_rsa,
110
+ self._encryption.ee_rsa)
111
+ # Register AES string for decryption on server side
112
+ self.request(16, 0, True, data=f'set {aes_string_encrypted}')
113
+ # Some auth request, might be redundant
114
+ response = self.request(7, 0, True)
115
+
116
+ def logout(self) -> None:
117
+ self.request(11, 0, True)
118
+
119
+ def get_firmware(self) -> Firmware:
120
+ text = '0|1,0,0'
121
+
122
+ body = self._encrypt_body(text)
123
+
124
+ response = self.request(2, 1, True, data=body)
125
+ response_text = self._decrypt_data(response.text)
126
+ device_datamap = dict(line.split(" ", 1) for line in response_text.split("\r\n")[1:-1])
127
+
128
+ return Firmware(parse.unquote(device_datamap['hardVer']), parse.unquote(device_datamap['modelName']),
129
+ parse.unquote(device_datamap['softVer']))
130
+
131
+ def get_status(self) -> Status:
132
+ mac_info_request = "1|1,0,0"
133
+ lan_ip_request = "4|1,0,0"
134
+ wan_ip_request = "23|1,0,0"
135
+ device_data_request = '13|1,0,0'
136
+ all_requests = [
137
+ mac_info_request, lan_ip_request, wan_ip_request, device_data_request,
138
+ RouterConstants.HOST_WIFI_2G_REQUEST, RouterConstants.HOST_WIFI_5G_REQUEST,
139
+ RouterConstants.GUEST_WIFI_2G_REQUEST, RouterConstants.GUEST_WIFI_5G_REQUEST,
140
+ RouterConstants.IOT_WIFI_2G_REQUEST, RouterConstants.IOT_WIFI_5G_REQUEST
141
+ ]
142
+ request_text = '#'.join(all_requests)
143
+ body = self._encrypt_body(request_text)
144
+
145
+ response = self.request(2, 1, True, data=body)
146
+ response_text = self._decrypt_data(response.text)
147
+
148
+ matches = TplinkC80Router.DATA_REGEX.findall(response_text)
149
+
150
+ data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
151
+
152
+ def extract_value(response_list, prefix):
153
+ return next((s.split(prefix, 1)[1] for s in response_list if s.startswith(prefix)), None)
154
+
155
+ network_info = {
156
+ 'lan_mac': extract_value(data_blocks[mac_info_request], "mac 0 "),
157
+ 'wan_mac': extract_value(data_blocks[mac_info_request], "mac 1 "),
158
+ 'lan_ip': extract_value(data_blocks[lan_ip_request], "ip "),
159
+ 'wan_ip': extract_value(data_blocks[wan_ip_request], "ip "),
160
+ 'gateway_ip': extract_value(data_blocks[wan_ip_request], "gateway "),
161
+ 'uptime': extract_value(data_blocks[wan_ip_request], "upTime ")
162
+ }
163
+
164
+ wifi_status = {key: extract_value(data_blocks[request], "bEnable ") == '1'
165
+ for key, request in RouterConstants.CONNECTION_REQUESTS_MAP.items()}
166
+
167
+ device_data_response = data_blocks[device_data_request]
168
+
169
+ mapped_devices = self._parse_devices(device_data_response)
170
+
171
+ status = Status()
172
+ status._wan_macaddr = EUI48(network_info['wan_mac'])
173
+ status._lan_macaddr = EUI48(network_info['lan_mac'])
174
+ status._lan_ipv4_addr = IPv4Address(network_info['lan_ip'])
175
+ status._wan_ipv4_addr = IPv4Address(network_info['wan_ip'])
176
+ status._wan_ipv4_gateway = IPv4Address(network_info['gateway_ip'])
177
+ status.wan_ipv4_uptime = int(network_info['uptime']) // 100
178
+
179
+ status.wifi_2g_enable = wifi_status[Connection.HOST_2G]
180
+ status.wifi_5g_enable = wifi_status[Connection.HOST_5G]
181
+ status.guest_2g_enable = wifi_status[Connection.GUEST_2G]
182
+ status.guest_5g_enable = wifi_status[Connection.GUEST_5G]
183
+ status.iot_2g_enable = wifi_status[Connection.IOT_2G]
184
+ status.iot_5g_enable = wifi_status[Connection.IOT_5G]
185
+
186
+ status.wired_total = sum(1 for device in mapped_devices if device.type == Connection.WIRED)
187
+ status.wifi_clients_total = sum(1 for device in mapped_devices
188
+ if device.type in (Connection.HOST_2G, Connection.HOST_5G))
189
+ status.guest_clients_total = sum(1 for device in mapped_devices
190
+ if device.type in (Connection.GUEST_2G, Connection.GUEST_5G))
191
+ status.iot_clients_total = sum(1 for device in mapped_devices
192
+ if device.type in (Connection.IOT_2G, Connection.IOT_5G))
193
+ status.clients_total = (status.wired_total + status.wifi_clients_total +
194
+ status.guest_clients_total + status.iot_clients_total)
195
+
196
+ status.devices = mapped_devices
197
+ return status
198
+
199
+ def reboot(self) -> None:
200
+ self.request(6, 1, True)
201
+
202
+ def set_wifi(self, wifi: Connection, enable: bool) -> None:
203
+ enable_string = f'bEnable {int(enable)}'
204
+ text = f'id {RouterConstants.CONNECTION_REQUESTS_MAP[wifi]}\r\n{enable_string}'
205
+ body = self._encrypt_body(text)
206
+ self.request(1, 0, True, data=body)
207
+
208
+ def get_ipv4_status(self) -> IPv4Status:
209
+ mac_info_request = "1|1,0,0"
210
+ lan_ip_request = "4|1,0,0"
211
+ dhcp_request = "8|1,0,0"
212
+ link_type_request = "22|1,0,0"
213
+ wan_ip_request = "23|1,0,0"
214
+ static_ip_request = "24|1,0,0"
215
+ all_requests = [
216
+ mac_info_request, lan_ip_request, dhcp_request, link_type_request, wan_ip_request, static_ip_request]
217
+ request_text = '#'.join(all_requests)
218
+ body = self._encrypt_body(request_text)
219
+
220
+ response = self.request(2, 1, True, data=body)
221
+ response_text = self._decrypt_data(response.text)
222
+
223
+ matches = TplinkC80Router.DATA_REGEX.findall(response_text)
224
+
225
+ data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
226
+
227
+ network_info = {
228
+ 'lan_mac': self._extract_value(data_blocks[mac_info_request], "mac 0 "),
229
+ 'wan_mac': self._extract_value(data_blocks[mac_info_request], "mac 1 "),
230
+ 'lan_ip': self._extract_value(data_blocks[lan_ip_request], "ip "),
231
+ 'wan_ip': self._extract_value(data_blocks[wan_ip_request], "ip "),
232
+ 'gateway_ip': self._extract_value(data_blocks[wan_ip_request], "gateway "),
233
+ 'uptime': self._extract_value(data_blocks[wan_ip_request], "upTime "),
234
+ 'wan_mask': self._extract_value(data_blocks[wan_ip_request], "mask "),
235
+ 'lan_mask': self._extract_value(data_blocks[lan_ip_request], "mask "),
236
+ 'dns_1': self._extract_value(data_blocks[wan_ip_request], "dns 0 "),
237
+ 'dns_2': self._extract_value(data_blocks[wan_ip_request], "dns 1 "),
238
+ 'dhcp_enabled': self._extract_value(data_blocks[dhcp_request], "enable "),
239
+ 'link_type': self._extract_value(data_blocks[link_type_request], "linkType "),
240
+ }
241
+
242
+ ipv4status = IPv4Status()
243
+ ipv4status._wan_macaddr = EUI48(network_info['wan_mac'])
244
+ ipv4status._wan_ipv4_ipaddr = IPv4Address(network_info['wan_ip'])
245
+ ipv4status._wan_ipv4_gateway = IPv4Address(network_info['gateway_ip'])
246
+ ipv4status._wan_ipv4_conntype = RouterConstants.CONNECTION_TYPE_MAP[network_info['link_type']]
247
+ ipv4status._wan_ipv4_netmask = IPv4Address(network_info['wan_mask'])
248
+ ipv4status._wan_ipv4_pridns = IPv4Address(network_info['dns_1'])
249
+ ipv4status._wan_ipv4_snddns = IPv4Address(network_info['dns_2'])
250
+ ipv4status._lan_macaddr = EUI48(network_info['lan_mac'])
251
+ ipv4status._lan_ipv4_ipaddr = IPv4Address(network_info['lan_ip'])
252
+ ipv4status.lan_ipv4_dhcp_enable = network_info['dhcp_enabled'] == '1'
253
+ ipv4status._lan_ipv4_netmask = IPv4Address(network_info['lan_mask'])
254
+ return ipv4status
255
+
256
+ def get_ipv4_reservations(self) -> list[IPv4Reservation]:
257
+ body = self._encrypt_body('12|1,0,0')
258
+
259
+ response = self.request(2, 1, True, data=body)
260
+ response_text = self._decrypt_data(response.text)
261
+ matches = TplinkC80Router.DATA_REGEX.findall(response_text)
262
+
263
+ data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
264
+ filtered_reservations = self._parse_response_to_dict(data_blocks['12|1,0,0'])
265
+
266
+ mapped_reservations: list[IPv4Reservation] = []
267
+ for reservation in filtered_reservations:
268
+ reservation_to_add = IPv4Reservation(EUI48(reservation['mac']), IPv4Address(reservation['ip']),
269
+ reservation['name'], reservation['dhcpsEnable'] == '1')
270
+ mapped_reservations.append(reservation_to_add)
271
+ return mapped_reservations
272
+
273
+ def get_dhcp_leases(self) -> list[IPv4DHCPLease]:
274
+ body = self._encrypt_body('9|1,0,0')
275
+
276
+ response = self.request(2, 1, True, data=body)
277
+ response_text = self._decrypt_data(response.text)
278
+ matches = TplinkC80Router.DATA_REGEX.findall(response_text)
279
+
280
+ data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
281
+
282
+ filtered_leases = self._parse_response_to_dict(data_blocks['9|1,0,0'])
283
+
284
+ mapped_leases: list[IPv4DHCPLease] = []
285
+ for lease in filtered_leases:
286
+ lease_to_add = IPv4DHCPLease(EUI48(lease['mac']), IPv4Address(lease['ip']),
287
+ lease['hostName'], f'expires {lease["expires"]}')
288
+ mapped_leases.append(lease_to_add)
289
+
290
+ return mapped_leases
291
+
292
+ def get_vpn_status(self) -> VPNStatus:
293
+ body = self._encrypt_body("22|1,0,0")
294
+
295
+ response = self.request(2, 1, True, data=body)
296
+ response_text = self._decrypt_data(response.text)
297
+ matches = TplinkC80Router.DATA_REGEX.findall(response_text)
298
+
299
+ data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
300
+
301
+ vpn_status = VPNStatus()
302
+ vpn_status.pptpvpn_enable = self._extract_value(data_blocks["22|1,0,0"], "linkType ") == '4'
303
+
304
+ return vpn_status
305
+
306
+ def _parse_devices(self, device_data_response: list[str]) -> list[Device]:
307
+ filtered_devices = self._parse_response_to_dict(device_data_response)
308
+
309
+ device_type_to_connection = {
310
+ 0: Connection.WIRED,
311
+ 1: Connection.HOST_2G, 2: Connection.GUEST_2G,
312
+ 3: Connection.HOST_5G, 4: Connection.GUEST_5G,
313
+ 13: Connection.IOT_2G, 14: Connection.IOT_5G
314
+ }
315
+
316
+ mapped_devices = []
317
+ for device in filtered_devices:
318
+ if device['online'] == '1':
319
+ device_type = int(device['type'])
320
+ connection_type = device_type_to_connection.get(device_type, Connection.UNKNOWN)
321
+ else:
322
+ connection_type = Connection.UNKNOWN
323
+
324
+ device_to_add = Device(connection_type, EUI48(device['mac']), IPv4Address(device['ip']), device['name'])
325
+ device_to_add.up_speed = int(device['up'])
326
+ device_to_add.down_speed = int(device['down'])
327
+ mapped_devices.append(device_to_add)
328
+ return mapped_devices
329
+
330
+ def _parse_response_to_dict(self, response_data: list[str]) -> list[dict]:
331
+ result_dict = defaultdict(dict)
332
+ for entry in response_data:
333
+ parts = entry.split(' ', 2)
334
+ key, id_str = parts[0], parts[1]
335
+ value = parts[2] if len(parts) == 3 else ''
336
+ result_dict[int(id_str)][key] = value
337
+
338
+ return [v for _, v in result_dict.items() if v.get("ip") != "0.0.0.0"]
339
+
340
+ @staticmethod
341
+ def _encrypt_password(pwd: str, key: str = RouterConfig.KEY, encoding: str = RouterConfig.ENCODING) -> str:
342
+ max_len = max(len(key), len(pwd))
343
+ pwd = pwd.ljust(max_len, RouterConfig.PAD_CHAR)
344
+ key = key.ljust(max_len, RouterConfig.PAD_CHAR)
345
+
346
+ result = []
347
+ for i in range(max_len):
348
+ result.append(encoding[(ord(pwd[i]) ^ ord(key[i])) % len(encoding)])
349
+
350
+ return "".join(result)
351
+
352
+ @staticmethod
353
+ def _encode_token(encoded_password: str, response: str) -> str:
354
+ response_text = response.text.splitlines()
355
+ auth_info1 = response_text[RouterConstants.AUTH_TOKEN_INDEX1]
356
+ auth_info2 = response_text[RouterConstants.AUTH_TOKEN_INDEX2]
357
+
358
+ encoded_token = TplinkC80Router._encrypt_password(encoded_password, auth_info1, auth_info2)
359
+ return parse.quote(encoded_token, safe='!()*')
360
+
361
+ def _get_signature(self, datalen: int) -> str:
362
+ encryption = self._encryption
363
+ r = f'{encryption.aes_string}&s={str(int(encryption.seq) + datalen)}'
364
+ e = ''
365
+ n = 0
366
+ while n < len(r):
367
+ e += EncryptionWrapper.rsa_encrypt(r[n:53], encryption.nn_rsa, encryption.ee_rsa)
368
+ n += 53
369
+ return e
370
+
371
+ def _encrypt_body(self, text: str) -> str:
372
+ encryption = self._encryption
373
+
374
+ key_bytes = encryption.key_aes.encode("utf-8")
375
+ iv_bytes = encryption.iv_aes.encode("utf-8")
376
+
377
+ cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
378
+ data = b64encode(cipher.encrypt(pad(text.encode("utf-8"), AES.block_size))).decode()
379
+
380
+ sign = self._get_signature(len(data))
381
+ return f'sign={sign}\r\ndata={data}'
382
+
383
+ def _decrypt_data(self, encrypted_text: str) -> str:
384
+ key_bytes = self._encryption.key_aes.encode("utf-8")
385
+ iv_bytes = self._encryption.iv_aes.encode("utf-8")
386
+
387
+ cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
388
+ decrypted_padded = cipher.decrypt(b64decode(encrypted_text))
389
+ return unpad(decrypted_padded, AES.block_size).decode("utf-8")
390
+
391
+ def _extract_value(self, response_list, prefix):
392
+ return next((s.split(prefix, 1)[1] for s in response_list if s.startswith(prefix)), None)
393
+
394
+ def request(self, code: int, asyn: int, use_token: bool = False, data: str = None):
395
+ url = f"{self.host}/?code={code}&asyn={asyn}"
396
+ if use_token:
397
+ url += f"&id={self._encryption.token}"
398
+ try:
399
+ response = self._session.post(url, data=data, timeout=self.timeout)
400
+ # Raises exception for 4XX/5XX status codes for all requests except 1st in authorize
401
+ if not (code == 2 and asyn == 1 and use_token is False and data is None):
402
+ response.raise_for_status()
403
+ return response
404
+ except requests.exceptions.RequestException as e:
405
+ self._logger.error(f"Network error: {e}")
406
+ raise ClientException(f"Network error: {str(e)}") from e
@@ -290,7 +290,8 @@ class TPLinkEXClient(TPLinkMRClientBase):
290
290
  ret_code = self._parse_ret_val(response)
291
291
  error = ''
292
292
  if ret_code == self.HTTP_ERR_USER_PWD_NOT_CORRECT:
293
- error = 'TplinkRouter - EX - Login failed, wrong user or password.'
293
+ error = ('TplinkRouter - EX - Login failed, wrong user or password. '
294
+ 'Try to pass user instead of admin in username')
294
295
  elif ret_code == self.HTTP_ERR_USER_BAD_REQUEST:
295
296
  error = 'TplinkRouter - EX - Login failed. Generic error code: {}'.format(ret_code)
296
297
  elif ret_code != self.HTTP_RET_OK:
@@ -7,9 +7,9 @@ from tplinkrouterc6u.client.deco import TPLinkDecoClient
7
7
  from tplinkrouterc6u.client_abstract import AbstractRouter
8
8
  from tplinkrouterc6u.client.mr import TPLinkMRClient
9
9
  from tplinkrouterc6u.client.ex import TPLinkEXClient
10
- from tplinkrouterc6u.client.c6v4 import TplinkC6V4Router
11
10
  from tplinkrouterc6u.client.c5400x import TplinkC5400XRouter
12
11
  from tplinkrouterc6u.client.c1200 import TplinkC1200Router
12
+ from tplinkrouterc6u.client.c80 import TplinkC80Router
13
13
  from tplinkrouterc6u.client.vr import TPLinkVRClient
14
14
 
15
15
 
@@ -17,8 +17,8 @@ class TplinkRouterProvider:
17
17
  @staticmethod
18
18
  def get_client(host: str, password: str, username: str = 'admin', logger: Logger = None,
19
19
  verify_ssl: bool = True, timeout: int = 30) -> AbstractRouter:
20
- for client in [TplinkC5400XRouter, TPLinkVRClient, TPLinkEXClient, TPLinkMRClient, TplinkC6V4Router,
21
- TPLinkDecoClient, TPLinkXDRClient, TplinkRouter]:
20
+ for client in [TplinkC5400XRouter, TPLinkVRClient, TPLinkEXClient, TPLinkMRClient, TPLinkDecoClient,
21
+ TPLinkXDRClient, TplinkRouter, TplinkC80Router]:
22
22
  router = client(host, password, username, logger, verify_ssl, timeout)
23
23
  if router.supports():
24
24
  return router
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tplinkrouterc6u
3
- Version: 5.5.0
3
+ Version: 5.6.0
4
4
  Summary: TP-Link Router API
5
5
  Home-page: https://github.com/AlexandrErohin/TP-Link-Archer-C6U
6
6
  Author: Alex Erohin
@@ -57,6 +57,7 @@ from tplinkrouterc6u import (
57
57
  TPLinkEXClient,
58
58
  TPLinkXDRClient,
59
59
  TPLinkDecoClient,
60
+ TplinkC80Router,
60
61
  Connection
61
62
  )
62
63
  from logging import Logger
@@ -313,6 +314,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
313
314
  - Archer C6 (v2.0, v3.0)
314
315
  - Archer C6U v1.0
315
316
  - Archer C7 (v4.0, v5.0)
317
+ - Archer C80 2.20
316
318
  - Archer C5400X V1
317
319
  - Archer GX90 v1.0
318
320
  - Archer MR200 (v5, v5.3, v6.0)
@@ -334,6 +336,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
334
336
  - Deco X60 V3
335
337
  - Deco X90
336
338
  - Deco XE75 (v1.0, v2.0)
339
+ - Deco XE75PRO (v3.0)
337
340
  - EX511 v2.0
338
341
  - HX510 v1.0
339
342
  - NX510v v1.0
@@ -344,6 +347,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
344
347
  - TL-MR6500v
345
348
  - TL-WA3001 v1.0
346
349
  - TL-XDR3010 V2
350
+ - XC220-G3v v2.30
347
351
 
348
352
  ### Not fully tested Hardware Versions
349
353
  - AD7200 V2
@@ -1,20 +1,22 @@
1
1
  test/__init__.py,sha256=McQmUjeN3AwmwdS6QNfwGXXE77OKoPK852I2BM9XsrU,210
2
2
  test/test_client_c1200.py,sha256=4RdwMEwTGWLOxOVTuPikifXDcUJFQ13DuyWGm2Aevrk,8811
3
3
  test/test_client_c6u.py,sha256=3nvhybxYxiCVjjLXB3nBYrAkXS8zoLtJibgvsqsTQ3U,32140
4
+ test/test_client_c80.py,sha256=G_DV8PV-TyQuzmRRiYCCFt5W3RjR-LAYFqPYRGFebrg,30325
4
5
  test/test_client_deco.py,sha256=YPLKRD8GoyDYHfRgdXvCk8iVNw8zdMJW-AHVnNbpdTM,31719
5
6
  test/test_client_ex.py,sha256=0dVvOJqxpR2xNca99vadfdpxmzW-4sk9X_4tAK8x9-c,21710
6
7
  test/test_client_mr.py,sha256=NxTgXv_wN50K8KsJhykoU-TX0ddDGlX_7jp50FXOLjg,33517
7
8
  test/test_client_xdr.py,sha256=mgn-xL5mD5sHD8DjTz9vpY7jeh4Ob6Um6Y8v5Qgx2jA,23374
8
- tplinkrouterc6u/__init__.py,sha256=8f-K4p6y_bIa4NReODulceWsjCCojAFSSj5vrjaogdw,953
9
+ tplinkrouterc6u/__init__.py,sha256=-YGlTq78D4jE7pTTs4xTW3NamoiSIrNtpswne5ohb0s,951
9
10
  tplinkrouterc6u/client_abstract.py,sha256=3UYzmll774S_Gb5E0FTVO_rI3-XFM7PSklg1-V-2jls,1419
10
- tplinkrouterc6u/provider.py,sha256=lgmGAZI5E1jgpo0IRY4TIKTbAb4S_wX3HhRcx0RoSmY,2083
11
+ tplinkrouterc6u/provider.py,sha256=FGwdxiebku-Bgr6OM9YZQqdus68t1twHw7eRPco2NjE,2080
11
12
  tplinkrouterc6u/client/__init__.py,sha256=KBy3fmtA9wgyFrb0Urh2x4CkKtWVnESdp-vxmuOvq0k,27
12
13
  tplinkrouterc6u/client/c1200.py,sha256=_nY_pJ-wPWODAaes9kHPdVcM6YM54f1E54CfdoFHqbE,4771
13
14
  tplinkrouterc6u/client/c5400x.py,sha256=9E0omBSbWY_ljrs5MTCMu5brmrLtzsDB5O62Db8lP8Q,4329
14
15
  tplinkrouterc6u/client/c6u.py,sha256=TbS8qbwfPzIVDEMNNlKMhWuGeo-RUvaCkG_bF7Y4L0Q,17637
15
16
  tplinkrouterc6u/client/c6v4.py,sha256=bvquZ0FHgU_LJfZxQkfyKQz0C52mFytC4mzFSOqp99Y,1596
17
+ tplinkrouterc6u/client/c80.py,sha256=OukfG_403sEhK6o8icw8jYMztCNM2T9kiWIJ5qOlAzI,18252
16
18
  tplinkrouterc6u/client/deco.py,sha256=7uVd9wt3aJGShft7L520AzFpQ85xbgRWM-8uyrpxE8Y,8229
17
- tplinkrouterc6u/client/ex.py,sha256=Ih0aCvjhUGN6MDIeRgT0nSBQYNWzH9c70i71AN_G_Ns,13039
19
+ tplinkrouterc6u/client/ex.py,sha256=ZK08mlOts3fbBJh9NUrzBADP0N9LHgzOXCKY2tF9gG0,13111
18
20
  tplinkrouterc6u/client/mr.py,sha256=DJ5phVte-yEMddE5QAUu2MnRBmfuBZwz2-4ZWYWXlI0,25956
19
21
  tplinkrouterc6u/client/vr.py,sha256=7Tbu0IrWtr4HHtyrnLFXEJi1QctzhilciL7agtwQ0R8,5025
20
22
  tplinkrouterc6u/client/xdr.py,sha256=9YuVfrmsrDeqnhJXp1Fc4OfcJEz9SkjdmpOoTIDi6Js,10327
@@ -24,8 +26,8 @@ tplinkrouterc6u/common/encryption.py,sha256=4HelTxzN6esMfDZRBt3m8bwB9Nj_biKijnCn
24
26
  tplinkrouterc6u/common/exception.py,sha256=_0G8ZvW5__CsGifHrsZeULdl8c6EUD071sDCQsQgrHY,140
25
27
  tplinkrouterc6u/common/helper.py,sha256=23b04fk9HuVinrZXMCS5R1rmF8uZ7eM-Cdnp7Br9NR0,572
26
28
  tplinkrouterc6u/common/package_enum.py,sha256=4ykL_2Pw0nDEIH_qR9UJlFF6stTgSfhPz32r8KT-sh8,1624
27
- tplinkrouterc6u-5.5.0.dist-info/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
28
- tplinkrouterc6u-5.5.0.dist-info/METADATA,sha256=tBsmDyFWC6vjlTys6G38pTZ658kQMgGhiGgTNplPyRw,15295
29
- tplinkrouterc6u-5.5.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
30
- tplinkrouterc6u-5.5.0.dist-info/top_level.txt,sha256=1iSCCIueqgEkrTxtQ-jiHe99jAB10zqrVdBcwvNfe_M,21
31
- tplinkrouterc6u-5.5.0.dist-info/RECORD,,
29
+ tplinkrouterc6u-5.6.0.dist-info/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
30
+ tplinkrouterc6u-5.6.0.dist-info/METADATA,sha256=0kLKHil_W04-wRDFwur1EjX-wbhgnB7LmeX5NpA0l6M,15374
31
+ tplinkrouterc6u-5.6.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
32
+ tplinkrouterc6u-5.6.0.dist-info/top_level.txt,sha256=1iSCCIueqgEkrTxtQ-jiHe99jAB10zqrVdBcwvNfe_M,21
33
+ tplinkrouterc6u-5.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5