tplinkrouterc6u 5.5.0__py3-none-any.whl → 5.6.1__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.
- test/test_client_c80.py +588 -0
- tplinkrouterc6u/__init__.py +1 -1
- tplinkrouterc6u/client/c80.py +410 -0
- tplinkrouterc6u/client/ex.py +2 -1
- tplinkrouterc6u/client/mr.py +2 -0
- tplinkrouterc6u/client/xdr.py +2 -0
- tplinkrouterc6u/provider.py +3 -3
- {tplinkrouterc6u-5.5.0.dist-info → tplinkrouterc6u-5.6.1.dist-info}/METADATA +18 -23
- {tplinkrouterc6u-5.5.0.dist-info → tplinkrouterc6u-5.6.1.dist-info}/RECORD +12 -11
- {tplinkrouterc6u-5.5.0.dist-info → tplinkrouterc6u-5.6.1.dist-info}/WHEEL +1 -1
- tplinkrouterc6u/client/c6v4.py +0 -41
- {tplinkrouterc6u-5.5.0.dist-info → tplinkrouterc6u-5.6.1.dist-info}/LICENSE +0 -0
- {tplinkrouterc6u-5.5.0.dist-info → tplinkrouterc6u-5.6.1.dist-info}/top_level.txt +0 -0
test/test_client_c80.py
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
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
|
+
STATUS_RESPONSE_IOT = ('00000\r\nid 1|1,0,0\r\nauthKey aaa3eKKee\r\nreserved\r\nsetWzd 8\r\nmode 1\r\n'
|
|
166
|
+
'logLevel 3\r\nfastpath 1\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01'
|
|
167
|
+
'\r\nwanMacType 0\r\nmodelMergeCursor 8\r\nid 4|1,0,0\r\nip 192.168.0.1\r\n'
|
|
168
|
+
'mask 255.255.255.0\r\nmode 0\r\nsmartIp 1\r\ngateway 192.168.0.1\r\nid 23|1,0,0'
|
|
169
|
+
'\r\nip 100.100.100.100\r\nmask 255.255.252.0\r\ngateway 100.100.105.1\r\n'
|
|
170
|
+
'dns 0 100.100.0.1\r\ndns 1 100.100.0.1\r\nstatus 1\r\ncode 0\r\nupTime 30814980'
|
|
171
|
+
'\r\ninPkts 3014881\r\ninOctets 1502000045\r\noutPkts 8216676\r\noutOctets 2500478193'
|
|
172
|
+
'\r\ninRates 337\r\noutRates 714\r\ndualMode 0\r\ndualIp 0.0.0.0\r\ndualMask 0.0.0.0'
|
|
173
|
+
'\r\ndualGateway 0.0.0.0\r\ndualDns 0 0.0.0.0\r\ndualDns 1 0.0.0.0\r\ndualCode 0'
|
|
174
|
+
'\r\ndualStatus 0\r\ninternetDnsDetect 1\r\nid 13|1,0,0\r\nip 0 192.168.0.1\r\n'
|
|
175
|
+
'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'
|
|
176
|
+
'\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'
|
|
177
|
+
'\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'
|
|
178
|
+
'\r\nmac 5 00-00-00-00-00-07\r\nbindEntry 0 0\r\nbindEntry 1 0\r\nbindEntry 2 0'
|
|
179
|
+
'\r\nbindEntry 3 0\r\nbindEntry 4 0\r\nbindEntry 5 0\r\nstaMgtEntry 0 0\r\n'
|
|
180
|
+
'staMgtEntry 1 0\r\nstaMgtEntry 2 0\r\nstaMgtEntry 3 0\r\nstaMgtEntry 4 1'
|
|
181
|
+
'\r\nstaMgtEntry 5 0\r\ntype 0 3\r\ntype 1 1\r\ntype 2 2\r\ntype 3 1\r\ntype 4 13'
|
|
182
|
+
'\r\ntype 5 0\r\nonline 0 0\r\nonline 1 0\r\nonline 2 1\r\nonline 3 1\r\nonline 4 1'
|
|
183
|
+
'\r\nonline 5 1\r\nblocked 0 0\r\nblocked 1 0\r\nblocked 2 0\r\nblocked 3 0\r\n'
|
|
184
|
+
'blocked 4 0\r\nblocked 5 0\r\nqosPrior 0 0\r\nqosPrior 1 0\r\nqosPrior 2 0\r\n'
|
|
185
|
+
'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'
|
|
186
|
+
'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'
|
|
187
|
+
'down 3 400\r\ndown 4 800\r\ndown 5 50\r\nupLimit 0 204800\r\nupLimit 1 204800\r\n'
|
|
188
|
+
'upLimit 2 204800\r\nupLimit 3 204800\r\nupLimit 4 204800\r\nupLimit 5 204800\r\n'
|
|
189
|
+
'downLimit 0 1048576\r\ndownLimit 1 1048576\r\ndownLimit 2 1048576\r\n'
|
|
190
|
+
'downLimit 3 1048576\r\ndownLimit 4 1048576\r\ndownLimit 5 1048576\r\nname 0 Laptop'
|
|
191
|
+
'\r\nname 1 iPhone\r\nname 2 Laptop2\r\nname 3 iPhone2\r\nname 4 IoT_thing\r\n'
|
|
192
|
+
'name 5 PC\r\nuBandwidth 0 0\r\nuBandwidth 1 0\r\nuBandwidth 2 0\r\nuBandwidth 3 0'
|
|
193
|
+
'\r\nuBandwidth 4 0\r\nuBandwidth 5 0\r\nuStandard 0 0\r\nuStandard 1 0\r\n'
|
|
194
|
+
'uStandard 2 0\r\nuStandard 3 2\r\nuStandard 4 2\r\nuStandard 5 0\r\ndevType 0 0'
|
|
195
|
+
'\r\ndevType 1 0\r\ndevType 2 0\r\ndevType 3 0\r\ndevType 4 0\r\ndevType 5 0\r\n'
|
|
196
|
+
'priTime 0 0\r\npriTime 1 0\r\npriTime 2 0\r\npriTime 3 0\r\npriTime 4 0\r\n'
|
|
197
|
+
'priTime 5 0\r\nleaseTime 0 0\r\nleaseTime 1 0\r\nleaseTime 2 0\r\nleaseTime 3 0'
|
|
198
|
+
'\r\nleaseTime 4 0\r\nleaseTime 5 0\r\ntotalVal 0 450\r\ntotalVal 1 5\r\n'
|
|
199
|
+
'totalVal 2 13\r\ntotalVal 3 956\r\ntotalVal 4 13\r\ntotalVal 5 53\r\ntotalUnit 0 2'
|
|
200
|
+
'\r\ntotalUnit 1 2\r\ntotalUnit 2 2\r\ntotalUnit 3 2\r\ntotalUnit 4 2\r\n'
|
|
201
|
+
'totalUnit 5 2\r\ndhcpsEntry 0 0\r\ndhcpsEntry 1 0\r\ndhcpsEntry 2 0\r\n'
|
|
202
|
+
'dhcpsEntry 3 0\r\ndhcpsEntry 4 0\r\ndhcpsEntry 5 0\r\nduration 0 0\r\n'
|
|
203
|
+
'duration 1 0\r\nduration 2 0\r\nduration 3 73\r\nduration 4 5388\r\n'
|
|
204
|
+
'duration 5 8559\r\ntxRate 0 0\r\ntxRate 1 0\r\ntxRate 2 0\r\ntxRate 3 156\r\n'
|
|
205
|
+
'txRate 4 130\r\ntxRate 5 0\r\nrxRate 0 0\r\nrxRate 1 0\r\nrxRate 2 0\r\n'
|
|
206
|
+
'rxRate 3 130\r\nrxRate 4 173\r\nrxRate 5 0\r\naveRssi 0 0\r\naveRssi 1 0\r\n'
|
|
207
|
+
'aveRssi 2 0\r\naveRssi 3 33\r\naveRssi 4 46\r\naveRssi 5 0\r\nslEnable 0 0\r\n'
|
|
208
|
+
'slEnable 1 0\r\nslEnable 2 0\r\nslEnable 3 0\r\nslEnable 4 0\r\nslEnable 5 0'
|
|
209
|
+
'\r\npriScheStatus 0 0\r\npriScheStatus 1 0\r\npriScheStatus 2 0\r\n'
|
|
210
|
+
'priScheStatus 3 0\r\npriScheStatus 4 0\r\npriScheStatus 5 0\r\nstart 0 0\r\n'
|
|
211
|
+
'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'
|
|
212
|
+
'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'
|
|
213
|
+
'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'
|
|
214
|
+
'startMin 2 0\r\nstartMin 3 0\r\nstartMin 4 0\r\nstartMin 5 0\r\nendMin 0 0\r\n'
|
|
215
|
+
'endMin 1 0\r\nendMin 2 0\r\nendMin 3 0\r\nendMin 4 0\r\nendMin 5 0\r\nrate 0 0'
|
|
216
|
+
'\r\nrate 1 0\r\nrate 2 0\r\nrate 3 156\r\nrate 4 173\r\nrate 5 0\r\ntxPkt 0 0'
|
|
217
|
+
'\r\ntxPkt 1 0\r\ntxPkt 2 0\r\ntxPkt 3 42602\r\ntxPkt 4 119683\r\ntxPkt 5 0'
|
|
218
|
+
'\r\nrxPkt 0 0\r\nrxPkt 1 0\r\nrxPkt 2 0\r\nrxPkt 3 6343\r\nrxPkt 4 275076\r\n'
|
|
219
|
+
'rxPkt 5 0\r\nid 33|1,1,0\r\nuUnit 0\r\ncSsidPrefix\r\nuRadiusIp 0.0.0.0\r\n'
|
|
220
|
+
'uRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
|
|
221
|
+
'\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
|
|
222
|
+
'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\n'
|
|
223
|
+
'bBcastSsid 1\r\ncSsid TP-Link\r\nbSecurityEnable 1\r\nuAuthType 3\r\nuWEPSecOpt 3'
|
|
224
|
+
'\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3'
|
|
225
|
+
'\r\ncRadiusSecret\r\ncPskSecret admin\r\nbSecCheck 0\r\nbEnabled 1\r\n'
|
|
226
|
+
'cUsrPIN 11100111\r\nbConfigured 1\r\nbIsLocked 0\r\nbEnRtPIN 1\r\n'
|
|
227
|
+
'bWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1'
|
|
228
|
+
'\r\nSecurityType 2\r\nbApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1'
|
|
229
|
+
'\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 1\r\nbTr069APEnable 0'
|
|
230
|
+
'\r\nbTr069SSIDEnable 0\r\nid 33|2,1,0\r\nuUnit 0\r\ncSsidPrefix\r\nuRadiusIp 0.0.0.0'
|
|
231
|
+
'\r\nuRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
|
|
232
|
+
'\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
|
|
233
|
+
'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 1\r\n'
|
|
234
|
+
'bBcastSsid 1\r\ncSsid TP-Link\r\nbSecurityEnable 1\r\nuAuthType 3\r\nuWEPSecOpt 3'
|
|
235
|
+
'\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 2\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3'
|
|
236
|
+
'\r\ncRadiusSecret\r\ncPskSecret admin\r\nbSecCheck 0\r\nbEnabled 1\r\n'
|
|
237
|
+
'cUsrPIN 11100111\r\nbConfigured 1\r\nbIsLocked 0\r\nbEnRtPIN 1\r\nbWifiBtnRecEnable 1'
|
|
238
|
+
'\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 1\r\nSecurityType 2\r\n'
|
|
239
|
+
'bApIsolated 0\r\neffectiveTime 0\r\nuMaxUploadSpeed -1\r\nuMaxDownloadSpeed -1\r\n'
|
|
240
|
+
'bwCtrlEnable 0\r\nenableBackup 1\r\nbTr069APEnable 0\r\nbTr069SSIDEnable 0\r\n'
|
|
241
|
+
'id 33|1,2,0\r\nuUnit 1\r\ncSsidPrefix Guest\r\nuRadiusIp 0.0.0.0\r\n'
|
|
242
|
+
'uRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\nuKeyLength 0 0\r\nuKeyLength 1 0'
|
|
243
|
+
'\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\ncKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\n'
|
|
244
|
+
'cKeyVal 3\r\nuRadiusPort 1812\r\nuKeyType 0\r\nuDefaultKey 1\r\nbEnable 0\r\n'
|
|
245
|
+
'bBcastSsid 1\r\ncSsid TP-Link_Guest\r\nbSecurityEnable 0\r\nuAuthType 3\r\n'
|
|
246
|
+
'uWEPSecOpt 3\r\nuRadiusSecOpt 3\r\nuPSKSecOpt 3\r\nuRadiusEncryptType 1\r\n'
|
|
247
|
+
'uPSKEncryptType 3\r\ncRadiusSecret\r\ncPskSecret\r\nbSecCheck 0\r\nbEnabled 1'
|
|
248
|
+
'\r\ncUsrPIN 11100111\r\nbConfigured 0\r\nbIsLocked 0\r\nbEnRtPIN 0\r\n'
|
|
249
|
+
'bWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\nbMwdsEnable 0\r\nbLanAccess 0'
|
|
250
|
+
'\r\nSecurityType 1\r\nbApIsolated 1\r\neffectiveTime 0\r\nuMaxUploadSpeed -1'
|
|
251
|
+
'\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 0\r\nbTr069APEnable 0'
|
|
252
|
+
'\r\nbTr069SSIDEnable 0\r\nid 33|2,2,0\r\nuUnit 1\r\ncSsidPrefix Guest\r\n'
|
|
253
|
+
'uRadiusIp 0.0.0.0\r\nuRadiusGKUpdateIntvl 0\r\nuPskGKUpdateIntvl 0\r\n'
|
|
254
|
+
'uKeyLength 0 0\r\nuKeyLength 1 0\r\nuKeyLength 2 0\r\nuKeyLength 3 0\r\n'
|
|
255
|
+
'cKeyVal 0\r\ncKeyVal 1\r\ncKeyVal 2\r\ncKeyVal 3\r\nuRadiusPort 1812\r\n'
|
|
256
|
+
'uKeyType 0\r\nuDefaultKey 1\r\nbEnable 0\r\nbBcastSsid 1\r\ncSsid TP-Link_Guest_5G'
|
|
257
|
+
'\r\nbSecurityEnable 0\r\nuAuthType 3\r\nuWEPSecOpt 3\r\nuRadiusSecOpt 3\r\n'
|
|
258
|
+
'uPSKSecOpt 3\r\nuRadiusEncryptType 1\r\nuPSKEncryptType 3\r\ncRadiusSecret\r\n'
|
|
259
|
+
'cPskSecret\r\nbSecCheck 0\r\nbEnabled 1\r\ncUsrPIN 11100111\r\nbConfigured 0\r\n'
|
|
260
|
+
'bIsLocked 0\r\nbEnRtPIN 0\r\nbWifiBtnRecEnable 1\r\nuVid 0\r\nbMumimo 0\r\n'
|
|
261
|
+
'bMwdsEnable 0\r\nbLanAccess 0\r\nSecurityType 1\r\nbApIsolated 1\r\neffectiveTime 0'
|
|
262
|
+
'\r\nuMaxUploadSpeed -1\r\nuMaxDownloadSpeed -1\r\nbwCtrlEnable 0\r\nenableBackup 0'
|
|
263
|
+
'\r\nbTr069APEnable 0\r\nbTr069SSIDEnable 0')
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class ResponseMock():
|
|
267
|
+
def __init__(self, text, status_code=0):
|
|
268
|
+
self.text = text
|
|
269
|
+
self.status_code = status_code
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class TplinkC80RouterTest(TplinkC80Router):
|
|
273
|
+
response = ''
|
|
274
|
+
|
|
275
|
+
def request(self, code: int, asyn: int, use_token: bool = False, data: str = None) -> dict | None:
|
|
276
|
+
|
|
277
|
+
# Responses
|
|
278
|
+
if code == 2 and asyn == 1:
|
|
279
|
+
if use_token is False:
|
|
280
|
+
if data == '0|1,0,0':
|
|
281
|
+
# Supports
|
|
282
|
+
return ResponseMock(self.response, 200)
|
|
283
|
+
else:
|
|
284
|
+
# Authorization
|
|
285
|
+
return ResponseMock('blabla\r\nblabla\r\nblabla\r\nauthinfo1\r\nauthinfo2')
|
|
286
|
+
elif use_token is True:
|
|
287
|
+
return ResponseMock(self.response)
|
|
288
|
+
elif (code == 16 or code == 7) and asyn == 0:
|
|
289
|
+
if use_token is False:
|
|
290
|
+
# Authorization
|
|
291
|
+
return ResponseMock('00000\r\n010001\r\nBC97577E65233B3E1137C61091D64176C334E52AD78FFBDDABC826B685435E'
|
|
292
|
+
'9D3DE83FE70C2AC62D6B13BD8EADA10B5623F9354DA0E99636A4F5519CA2DC2DC3\r\n12345656')
|
|
293
|
+
elif use_token is True:
|
|
294
|
+
# Authorization
|
|
295
|
+
return ResponseMock('00000')
|
|
296
|
+
|
|
297
|
+
raise ClientException()
|
|
298
|
+
|
|
299
|
+
def set_encrypted_response(self, response_text) -> None:
|
|
300
|
+
self.response = self._encrypt_body(response_text).split('data=')[1]
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class TestTPLinkClient(TestCase):
|
|
304
|
+
|
|
305
|
+
def test_supports(self) -> None:
|
|
306
|
+
response = ('00000\r\nid 0|1,0,0\r\nfullName AC1900%20MU-MIMO%20Wi-Fi%20Router\r\nfacturer TP-Link\r\nmodelName'
|
|
307
|
+
' Archer%20C80\r\nmodelVer 2.20\r\nsoftVer 1.13.15%20Build%20240812%20Rel.53972n(4555)\r\nhardVer'
|
|
308
|
+
' Archer%20C80%202.20\r\nspecialId 0x5545\r\ncountryCode 0x455a\r\nmainVer 0x5a010d0f\r\nminorVer'
|
|
309
|
+
' 0x1\r\nfacturerType 0')
|
|
310
|
+
|
|
311
|
+
client = TplinkC80RouterTest('', '')
|
|
312
|
+
client.response = response
|
|
313
|
+
supports = client.supports()
|
|
314
|
+
self.assertTrue(supports)
|
|
315
|
+
|
|
316
|
+
def test_authorize(self) -> None:
|
|
317
|
+
client = TplinkC80RouterTest('', '')
|
|
318
|
+
client.authorize()
|
|
319
|
+
|
|
320
|
+
encryption = client._encryption
|
|
321
|
+
self.assertEqual(encryption.ee_rsa, '010001')
|
|
322
|
+
self.assertEqual(encryption.nn_rsa, 'BC97577E65233B3E1137C61091D64176C334E52AD78FFBDDABC826B685435E9D3DE83FE70C'
|
|
323
|
+
'2AC62D6B13BD8EADA10B5623F9354DA0E99636A4F5519CA2DC2DC3')
|
|
324
|
+
self.assertEqual(encryption.seq, '12345656')
|
|
325
|
+
|
|
326
|
+
def test_get_firmware(self) -> None:
|
|
327
|
+
response = ('00000\r\nid 0|1,0,0\r\nfullName AC1900%20MU-MIMO%20Wi-Fi%20Router\r\nfacturer TP-Link\r\nmodelName'
|
|
328
|
+
' Archer%20C80\r\nmodelVer 2.20\r\nsoftVer 1.13.15%20Build%20240812%20Rel.53972n(4555)\r\nhardVer'
|
|
329
|
+
' Archer%20C80%202.20\r\nspecialId 0x5545\r\ncountryCode 0x455a\r\nmainVer 0x5a010d0f\r\nminorVer'
|
|
330
|
+
' 0x1\r\nfacturerType 0')
|
|
331
|
+
|
|
332
|
+
client = TplinkC80RouterTest('', '')
|
|
333
|
+
client.authorize()
|
|
334
|
+
|
|
335
|
+
client.set_encrypted_response(response)
|
|
336
|
+
|
|
337
|
+
firmware = client.get_firmware()
|
|
338
|
+
|
|
339
|
+
self.assertIsInstance(firmware, Firmware)
|
|
340
|
+
self.assertEqual(firmware.hardware_version, 'Archer C80 2.20')
|
|
341
|
+
self.assertEqual(firmware.model, 'Archer C80')
|
|
342
|
+
self.assertEqual(firmware.firmware_version, '1.13.15 Build 240812 Rel.53972n(4555)')
|
|
343
|
+
|
|
344
|
+
def test_get_ipv4_status(self) -> None:
|
|
345
|
+
|
|
346
|
+
client = TplinkC80RouterTest('', '')
|
|
347
|
+
client.authorize()
|
|
348
|
+
|
|
349
|
+
client.set_encrypted_response(IPV4_STATUS_RESPONSE)
|
|
350
|
+
|
|
351
|
+
ipv4_status: IPv4Status = client.get_ipv4_status()
|
|
352
|
+
|
|
353
|
+
self.assertIsInstance(ipv4_status, IPv4Status)
|
|
354
|
+
self.assertEqual(ipv4_status.wan_macaddress, EUI48('00-00-00-00-00-01'))
|
|
355
|
+
self.assertEqual(ipv4_status._wan_ipv4_ipaddr, IPv4Address('1.0.1.1'))
|
|
356
|
+
self.assertEqual(ipv4_status._wan_ipv4_gateway, IPv4Address('1.0.0.1'))
|
|
357
|
+
self.assertEqual(ipv4_status._wan_ipv4_conntype, 'Dynamic IP')
|
|
358
|
+
self.assertEqual(ipv4_status._wan_ipv4_netmask, IPv4Address('255.255.252.0'))
|
|
359
|
+
self.assertEqual(ipv4_status._wan_ipv4_pridns, IPv4Address('8.8.8.8'))
|
|
360
|
+
self.assertEqual(ipv4_status._wan_ipv4_snddns, IPv4Address('8.8.8.8'))
|
|
361
|
+
self.assertEqual(ipv4_status._lan_macaddr, EUI48('00-00-00-00-00-00'))
|
|
362
|
+
self.assertEqual(ipv4_status._lan_ipv4_ipaddr, IPv4Address('192.168.0.1'))
|
|
363
|
+
self.assertEqual(ipv4_status.lan_ipv4_dhcp_enable, True)
|
|
364
|
+
self.assertEqual(ipv4_status._lan_ipv4_netmask, IPv4Address('255.255.255.0'))
|
|
365
|
+
|
|
366
|
+
def test_get_ipv4_reservations(self) -> None:
|
|
367
|
+
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'
|
|
368
|
+
'mac 1 00-00-00-00-00-01\r\nreserved 0\r\nreserved 1\r\nbindEntry 0 0\r\nbindEntry 1 0\r\n'
|
|
369
|
+
'staMgtEntry 0 0\r\nstaMgtEntry 1 1\r\nname 0 Galaxy-S21\r\nname 1 Camera\r\nreserved_name 0\r\n'
|
|
370
|
+
'reserved_name 1\r\nblocked 0 0\r\nblocked 1 0\r\nupLimit 0 0\r\nupLimit 1 0\r\ndownLimit 0 0'
|
|
371
|
+
'\r\ndownLimit 1 0\r\nqosEntry 0 0\r\nqosEntry 1 0\r\npriTime 0 0\r\npriTime 1 0\r\n'
|
|
372
|
+
'dhcpsEntry 0 1\r\ndhcpsEntry 1 0\r\ndhcpsEnable 0 1\r\ndhcpsEnable 1 0\r\nslEnable 0 0\r\n'
|
|
373
|
+
'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'
|
|
374
|
+
'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'
|
|
375
|
+
'reserved2 0 0\r\nreserved2 1 0\r\ndisable 1')
|
|
376
|
+
|
|
377
|
+
client = TplinkC80RouterTest('', '')
|
|
378
|
+
client.authorize()
|
|
379
|
+
|
|
380
|
+
client.set_encrypted_response(response)
|
|
381
|
+
|
|
382
|
+
ipv4_reservations: list[IPv4Reservation] = client.get_ipv4_reservations()
|
|
383
|
+
ipv4_reservation: IPv4Reservation = ipv4_reservations[0]
|
|
384
|
+
|
|
385
|
+
self.assertIsInstance(ipv4_reservation, IPv4Reservation)
|
|
386
|
+
self.assertEqual(ipv4_reservation.macaddress, EUI48('00-00-00-00-00-00'))
|
|
387
|
+
self.assertEqual(ipv4_reservation.ipaddress, IPv4Address('192.168.0.112'))
|
|
388
|
+
self.assertEqual(ipv4_reservation.hostname, 'Galaxy-S21')
|
|
389
|
+
self.assertEqual(ipv4_reservation.enabled, True)
|
|
390
|
+
|
|
391
|
+
def test_get_dhcp_leases(self) -> None:
|
|
392
|
+
response = ('00000\r\nid 9|1,0,0\r\nhostName 0 Galaxy-S21\r\nhostName 1 iPhone\r\nhostName 2 PC\r\n'
|
|
393
|
+
'hostName 3 Laptop\r\nmac 0 00-00-00-00-00-00\r\nmac 1 00-00-00-00-00-01\r\n'
|
|
394
|
+
'mac 2 00-00-00-00-00-02\r\nmac 3 00-00-00-00-00-03\r\nreserved 0\r\nreserved 1'
|
|
395
|
+
'\r\nreserved 2\r\nreserved 3\r\nstate 0 5\r\nstate 1 5\r\nstate 2 5\r\nstate 3 5'
|
|
396
|
+
'\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'
|
|
397
|
+
'\r\nexpires 0 4294967295\r\nexpires 1 3669\r\nexpires 2 4025\r\nexpires 3 4202')
|
|
398
|
+
|
|
399
|
+
client = TplinkC80RouterTest('', '')
|
|
400
|
+
client.authorize()
|
|
401
|
+
|
|
402
|
+
client.set_encrypted_response(response)
|
|
403
|
+
|
|
404
|
+
dhcp_leases: list[IPv4DHCPLease] = client.get_dhcp_leases()
|
|
405
|
+
|
|
406
|
+
self.assertIsInstance(dhcp_leases[0], IPv4DHCPLease)
|
|
407
|
+
self.assertEqual(dhcp_leases[0].macaddress, EUI48('00-00-00-00-00-00'))
|
|
408
|
+
self.assertEqual(dhcp_leases[0].ipaddress, IPv4Address('192.168.0.112'))
|
|
409
|
+
self.assertEqual(dhcp_leases[0].hostname, 'Galaxy-S21')
|
|
410
|
+
self.assertEqual(dhcp_leases[0].lease_time, 'expires 4294967295')
|
|
411
|
+
|
|
412
|
+
self.assertIsInstance(dhcp_leases[1], IPv4DHCPLease)
|
|
413
|
+
self.assertEqual(dhcp_leases[1].macaddress, EUI48('00-00-00-00-00-01'))
|
|
414
|
+
self.assertEqual(dhcp_leases[1].ipaddress, IPv4Address('192.168.0.101'))
|
|
415
|
+
self.assertEqual(dhcp_leases[1].hostname, 'iPhone')
|
|
416
|
+
self.assertEqual(dhcp_leases[1].lease_time, 'expires 3669')
|
|
417
|
+
|
|
418
|
+
def test_get_vpn_status(self) -> None:
|
|
419
|
+
response = '00000\r\nid 22|1,0,0\r\nenable 1\r\nneedPnpDetect 0\r\nocnDetect 0\r\n' \
|
|
420
|
+
'reserved\r\nlinkMode 0\r\nlinkType 4'
|
|
421
|
+
|
|
422
|
+
client = TplinkC80RouterTest('', '')
|
|
423
|
+
client.authorize()
|
|
424
|
+
|
|
425
|
+
client.set_encrypted_response(response)
|
|
426
|
+
|
|
427
|
+
vpn_status: VPNStatus = client.get_vpn_status()
|
|
428
|
+
|
|
429
|
+
self.assertIsInstance(vpn_status, VPNStatus)
|
|
430
|
+
self.assertEqual(vpn_status.openvpn_clients_total, 0)
|
|
431
|
+
self.assertEqual(vpn_status.pptpvpn_clients_total, 0)
|
|
432
|
+
self.assertEqual(vpn_status.pptpvpn_enable, True)
|
|
433
|
+
|
|
434
|
+
def test_get_status(self) -> None:
|
|
435
|
+
client = TplinkC80RouterTest('', '')
|
|
436
|
+
client.authorize()
|
|
437
|
+
|
|
438
|
+
client.set_encrypted_response(STATUS_RESPONSE_TEXT)
|
|
439
|
+
status = client.get_status()
|
|
440
|
+
|
|
441
|
+
self.assertIsInstance(status, Status)
|
|
442
|
+
self.assertEqual(status.wan_macaddr, '00-00-00-00-00-01')
|
|
443
|
+
self.assertIsInstance(status.wan_macaddress, EUI48)
|
|
444
|
+
self.assertEqual(status.lan_macaddr, '00-00-00-00-00-00')
|
|
445
|
+
self.assertIsInstance(status.lan_macaddress, EUI48)
|
|
446
|
+
self.assertEqual(status.wan_ipv4_addr, '100.100.100.100')
|
|
447
|
+
self.assertIsInstance(status.lan_ipv4_address, IPv4Address)
|
|
448
|
+
self.assertEqual(status.lan_ipv4_addr, '192.168.0.1')
|
|
449
|
+
self.assertEqual(status.wan_ipv4_gateway, '100.100.105.1')
|
|
450
|
+
self.assertIsInstance(status.wan_ipv4_address, IPv4Address)
|
|
451
|
+
self.assertEqual(status.wired_total, 1)
|
|
452
|
+
self.assertEqual(status.wifi_clients_total, 1)
|
|
453
|
+
self.assertEqual(status.guest_clients_total, 1)
|
|
454
|
+
self.assertEqual(status.clients_total, 4)
|
|
455
|
+
self.assertEqual(status.iot_clients_total, 1)
|
|
456
|
+
self.assertFalse(status.guest_2g_enable)
|
|
457
|
+
self.assertFalse(status.guest_5g_enable)
|
|
458
|
+
self.assertTrue(status.iot_2g_enable)
|
|
459
|
+
self.assertTrue(status.iot_5g_enable)
|
|
460
|
+
self.assertTrue(status.wifi_2g_enable)
|
|
461
|
+
self.assertTrue(status.wifi_5g_enable)
|
|
462
|
+
self.assertEqual(status.wan_ipv4_uptime, 308149)
|
|
463
|
+
self.assertEqual(status.mem_usage, None)
|
|
464
|
+
self.assertEqual(status.cpu_usage, None)
|
|
465
|
+
self.assertEqual(len(status.devices), 6)
|
|
466
|
+
|
|
467
|
+
device = status.devices[0]
|
|
468
|
+
self.assertIsInstance(device, Device)
|
|
469
|
+
self.assertEqual(device.type, Connection.UNKNOWN)
|
|
470
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-02')
|
|
471
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
472
|
+
self.assertEqual(device.ipaddr, '192.168.0.1')
|
|
473
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
474
|
+
self.assertEqual(device.hostname, 'Laptop')
|
|
475
|
+
self.assertEqual(device.up_speed, 0)
|
|
476
|
+
self.assertEqual(device.down_speed, 0)
|
|
477
|
+
|
|
478
|
+
device = status.devices[1]
|
|
479
|
+
self.assertIsInstance(device, Device)
|
|
480
|
+
self.assertEqual(device.type, Connection.UNKNOWN)
|
|
481
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-03')
|
|
482
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
483
|
+
self.assertEqual(device.ipaddr, '192.168.0.2')
|
|
484
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
485
|
+
self.assertEqual(device.hostname, 'iPhone')
|
|
486
|
+
self.assertEqual(device.up_speed, 0)
|
|
487
|
+
self.assertEqual(device.down_speed, 0)
|
|
488
|
+
|
|
489
|
+
device = status.devices[2]
|
|
490
|
+
self.assertIsInstance(device, Device)
|
|
491
|
+
self.assertEqual(device.type, Connection.GUEST_2G)
|
|
492
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-04')
|
|
493
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
494
|
+
self.assertEqual(device.ipaddr, '192.168.0.3')
|
|
495
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
496
|
+
self.assertEqual(device.hostname, 'Laptop2')
|
|
497
|
+
self.assertEqual(device.up_speed, 30)
|
|
498
|
+
self.assertEqual(device.down_speed, 200)
|
|
499
|
+
|
|
500
|
+
device = status.devices[3]
|
|
501
|
+
self.assertIsInstance(device, Device)
|
|
502
|
+
self.assertEqual(device.type, Connection.HOST_2G)
|
|
503
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-05')
|
|
504
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
505
|
+
self.assertEqual(device.ipaddr, '192.168.0.4')
|
|
506
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
507
|
+
self.assertEqual(device.hostname, 'iPhone2')
|
|
508
|
+
self.assertEqual(device.up_speed, 800)
|
|
509
|
+
self.assertEqual(device.down_speed, 400)
|
|
510
|
+
|
|
511
|
+
device = status.devices[4]
|
|
512
|
+
self.assertIsInstance(device, Device)
|
|
513
|
+
self.assertEqual(device.type, Connection.IOT_2G)
|
|
514
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-06')
|
|
515
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
516
|
+
self.assertEqual(device.ipaddr, '192.168.0.5')
|
|
517
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
518
|
+
self.assertEqual(device.hostname, 'IoT_thing')
|
|
519
|
+
self.assertEqual(device.up_speed, 1824)
|
|
520
|
+
self.assertEqual(device.down_speed, 800)
|
|
521
|
+
|
|
522
|
+
device = status.devices[5]
|
|
523
|
+
self.assertIsInstance(device, Device)
|
|
524
|
+
self.assertEqual(device.type, Connection.WIRED)
|
|
525
|
+
self.assertEqual(device.macaddr, '00-00-00-00-00-07')
|
|
526
|
+
self.assertIsInstance(device.macaddress, EUI48)
|
|
527
|
+
self.assertEqual(device.ipaddr, '192.168.0.6')
|
|
528
|
+
self.assertIsInstance(device.ipaddress, IPv4Address)
|
|
529
|
+
self.assertEqual(device.hostname, 'PC')
|
|
530
|
+
self.assertEqual(device.up_speed, 600)
|
|
531
|
+
self.assertEqual(device.down_speed, 50)
|
|
532
|
+
|
|
533
|
+
def test_get_status_without_iot(self) -> None:
|
|
534
|
+
client = TplinkC80RouterTest('', '')
|
|
535
|
+
client.authorize()
|
|
536
|
+
|
|
537
|
+
client.set_encrypted_response(STATUS_RESPONSE_IOT)
|
|
538
|
+
status = client.get_status()
|
|
539
|
+
|
|
540
|
+
self.assertIsInstance(status, Status)
|
|
541
|
+
self.assertEqual(status.wan_macaddr, '00-00-00-00-00-01')
|
|
542
|
+
self.assertIsInstance(status.wan_macaddress, EUI48)
|
|
543
|
+
self.assertEqual(status.lan_macaddr, '00-00-00-00-00-00')
|
|
544
|
+
self.assertIsInstance(status.lan_macaddress, EUI48)
|
|
545
|
+
self.assertEqual(status.wan_ipv4_addr, '100.100.100.100')
|
|
546
|
+
self.assertIsInstance(status.lan_ipv4_address, IPv4Address)
|
|
547
|
+
self.assertEqual(status.lan_ipv4_addr, '192.168.0.1')
|
|
548
|
+
self.assertEqual(status.wan_ipv4_gateway, '100.100.105.1')
|
|
549
|
+
self.assertIsInstance(status.wan_ipv4_address, IPv4Address)
|
|
550
|
+
self.assertEqual(status.wired_total, 1)
|
|
551
|
+
self.assertEqual(status.wifi_clients_total, 1)
|
|
552
|
+
self.assertEqual(status.guest_clients_total, 1)
|
|
553
|
+
self.assertEqual(status.clients_total, 4)
|
|
554
|
+
self.assertEqual(status.iot_clients_total, 1)
|
|
555
|
+
self.assertFalse(status.guest_2g_enable)
|
|
556
|
+
self.assertFalse(status.guest_5g_enable)
|
|
557
|
+
self.assertIsNone(status.iot_2g_enable)
|
|
558
|
+
self.assertIsNone(status.iot_5g_enable)
|
|
559
|
+
self.assertTrue(status.wifi_2g_enable)
|
|
560
|
+
self.assertTrue(status.wifi_5g_enable)
|
|
561
|
+
self.assertEqual(status.wan_ipv4_uptime, 308149)
|
|
562
|
+
self.assertEqual(status.mem_usage, None)
|
|
563
|
+
self.assertEqual(status.cpu_usage, None)
|
|
564
|
+
self.assertEqual(len(status.devices), 6)
|
|
565
|
+
|
|
566
|
+
def test_get_ipv4(self) -> None:
|
|
567
|
+
client = TplinkC80RouterTest('', '')
|
|
568
|
+
client.authorize()
|
|
569
|
+
|
|
570
|
+
client.set_encrypted_response(IPV4_RESPONSE_TEXT)
|
|
571
|
+
ipv4status: IPv4Status = client.get_ipv4_status()
|
|
572
|
+
|
|
573
|
+
self.assertIsInstance(ipv4status, IPv4Status)
|
|
574
|
+
self.assertEqual(ipv4status._wan_macaddr, EUI48('00-00-00-00-00-01'))
|
|
575
|
+
self.assertEqual(ipv4status._wan_ipv4_ipaddr, IPv4Address('1.1.1.1'))
|
|
576
|
+
self.assertEqual(ipv4status._wan_ipv4_gateway, IPv4Address('1.1.1.2'))
|
|
577
|
+
self.assertEqual(ipv4status._wan_ipv4_conntype, 'Dynamic IP')
|
|
578
|
+
self.assertEqual(ipv4status._wan_ipv4_netmask, IPv4Address('255.255.252.0'))
|
|
579
|
+
self.assertEqual(ipv4status._wan_ipv4_pridns, IPv4Address('5.8.8.8'))
|
|
580
|
+
self.assertEqual(ipv4status._wan_ipv4_snddns, IPv4Address('5.8.8.8'))
|
|
581
|
+
self.assertEqual(ipv4status._lan_macaddr, EUI48('00-00-00-00-00-00'))
|
|
582
|
+
self.assertEqual(ipv4status._lan_ipv4_ipaddr, IPv4Address('192.168.0.1'))
|
|
583
|
+
self.assertEqual(ipv4status.lan_ipv4_dhcp_enable, True)
|
|
584
|
+
self.assertEqual(ipv4status._lan_ipv4_netmask, IPv4Address('255.255.255.0'))
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
if __name__ == '__main__':
|
|
588
|
+
main()
|
tplinkrouterc6u/__init__.py
CHANGED
|
@@ -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.
|
|
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,410 @@
|
|
|
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
|
+
if self._verify_ssl is False:
|
|
81
|
+
self._session.verify = False
|
|
82
|
+
self._encryption = EncryptionState()
|
|
83
|
+
|
|
84
|
+
def supports(self) -> bool:
|
|
85
|
+
response = self.request(2, 1, data='0|1,0,0')
|
|
86
|
+
return response.status_code == 200 and response.text.startswith('00000')
|
|
87
|
+
|
|
88
|
+
def authorize(self) -> None:
|
|
89
|
+
encoded_password = TplinkC80Router._encrypt_password(self.password)
|
|
90
|
+
|
|
91
|
+
# Get token encryption strings and encrypt the password
|
|
92
|
+
response = self.request(2, 1)
|
|
93
|
+
self._encryption.token = TplinkC80Router._encode_token(encoded_password, response)
|
|
94
|
+
|
|
95
|
+
# Get RSA exponent, modulus and sequence number
|
|
96
|
+
response = self.request(16, 0, data='get')
|
|
97
|
+
|
|
98
|
+
responseText = response.text.splitlines()
|
|
99
|
+
if len(responseText) < 4:
|
|
100
|
+
raise ClientException("Invalid response for RSA keys from router")
|
|
101
|
+
self._encryption.ee_rsa = responseText[1]
|
|
102
|
+
self._encryption.nn_rsa = responseText[2]
|
|
103
|
+
self._encryption.seq = responseText[3]
|
|
104
|
+
|
|
105
|
+
# Generate key and initialization vector
|
|
106
|
+
self._encryption.key_aes = RouterConstants.DEFAULT_AES_VALUE
|
|
107
|
+
self._encryption.iv_aes = RouterConstants.DEFAULT_AES_VALUE
|
|
108
|
+
self._encryption.aes_string = f'k={self._encryption.key_aes}&i={self._encryption.iv_aes}'
|
|
109
|
+
|
|
110
|
+
# Encrypt AES string
|
|
111
|
+
aes_string_encrypted = EncryptionWrapper.rsa_encrypt(self._encryption.aes_string, self._encryption.nn_rsa,
|
|
112
|
+
self._encryption.ee_rsa)
|
|
113
|
+
# Register AES string for decryption on server side
|
|
114
|
+
self.request(16, 0, True, data=f'set {aes_string_encrypted}')
|
|
115
|
+
# Some auth request, might be redundant
|
|
116
|
+
response = self.request(7, 0, True)
|
|
117
|
+
|
|
118
|
+
def logout(self) -> None:
|
|
119
|
+
self.request(11, 0, True)
|
|
120
|
+
|
|
121
|
+
def get_firmware(self) -> Firmware:
|
|
122
|
+
text = '0|1,0,0'
|
|
123
|
+
|
|
124
|
+
body = self._encrypt_body(text)
|
|
125
|
+
|
|
126
|
+
response = self.request(2, 1, True, data=body)
|
|
127
|
+
response_text = self._decrypt_data(response.text)
|
|
128
|
+
device_datamap = dict(line.split(" ", 1) for line in response_text.split("\r\n")[1:-1])
|
|
129
|
+
|
|
130
|
+
return Firmware(parse.unquote(device_datamap['hardVer']), parse.unquote(device_datamap['modelName']),
|
|
131
|
+
parse.unquote(device_datamap['softVer']))
|
|
132
|
+
|
|
133
|
+
def get_status(self) -> Status:
|
|
134
|
+
mac_info_request = "1|1,0,0"
|
|
135
|
+
lan_ip_request = "4|1,0,0"
|
|
136
|
+
wan_ip_request = "23|1,0,0"
|
|
137
|
+
device_data_request = '13|1,0,0'
|
|
138
|
+
all_requests = [
|
|
139
|
+
mac_info_request, lan_ip_request, wan_ip_request, device_data_request,
|
|
140
|
+
RouterConstants.HOST_WIFI_2G_REQUEST, RouterConstants.HOST_WIFI_5G_REQUEST,
|
|
141
|
+
RouterConstants.GUEST_WIFI_2G_REQUEST, RouterConstants.GUEST_WIFI_5G_REQUEST,
|
|
142
|
+
RouterConstants.IOT_WIFI_2G_REQUEST, RouterConstants.IOT_WIFI_5G_REQUEST
|
|
143
|
+
]
|
|
144
|
+
request_text = '#'.join(all_requests)
|
|
145
|
+
body = self._encrypt_body(request_text)
|
|
146
|
+
|
|
147
|
+
response = self.request(2, 1, True, data=body)
|
|
148
|
+
response_text = self._decrypt_data(response.text)
|
|
149
|
+
|
|
150
|
+
matches = TplinkC80Router.DATA_REGEX.findall(response_text)
|
|
151
|
+
|
|
152
|
+
data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
|
|
153
|
+
|
|
154
|
+
def extract_value(response_list, prefix):
|
|
155
|
+
return next((s.split(prefix, 1)[1] for s in response_list if s.startswith(prefix)), None)
|
|
156
|
+
|
|
157
|
+
network_info = {
|
|
158
|
+
'lan_mac': extract_value(data_blocks[mac_info_request], "mac 0 "),
|
|
159
|
+
'wan_mac': extract_value(data_blocks[mac_info_request], "mac 1 "),
|
|
160
|
+
'lan_ip': extract_value(data_blocks[lan_ip_request], "ip "),
|
|
161
|
+
'wan_ip': extract_value(data_blocks[wan_ip_request], "ip "),
|
|
162
|
+
'gateway_ip': extract_value(data_blocks[wan_ip_request], "gateway "),
|
|
163
|
+
'uptime': extract_value(data_blocks[wan_ip_request], "upTime ")
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
wifi_status = {}
|
|
167
|
+
for key, request in RouterConstants.CONNECTION_REQUESTS_MAP.items():
|
|
168
|
+
value = data_blocks.get(request)
|
|
169
|
+
wifi_status[key] = extract_value(data_blocks.get(request), "bEnable ") == '1' if value else None
|
|
170
|
+
|
|
171
|
+
device_data_response = data_blocks[device_data_request]
|
|
172
|
+
|
|
173
|
+
mapped_devices = self._parse_devices(device_data_response)
|
|
174
|
+
|
|
175
|
+
status = Status()
|
|
176
|
+
status._wan_macaddr = EUI48(network_info['wan_mac'])
|
|
177
|
+
status._lan_macaddr = EUI48(network_info['lan_mac'])
|
|
178
|
+
status._lan_ipv4_addr = IPv4Address(network_info['lan_ip'])
|
|
179
|
+
status._wan_ipv4_addr = IPv4Address(network_info['wan_ip'])
|
|
180
|
+
status._wan_ipv4_gateway = IPv4Address(network_info['gateway_ip'])
|
|
181
|
+
status.wan_ipv4_uptime = int(network_info['uptime']) // 100
|
|
182
|
+
|
|
183
|
+
status.wifi_2g_enable = wifi_status[Connection.HOST_2G]
|
|
184
|
+
status.wifi_5g_enable = wifi_status[Connection.HOST_5G]
|
|
185
|
+
status.guest_2g_enable = wifi_status[Connection.GUEST_2G]
|
|
186
|
+
status.guest_5g_enable = wifi_status[Connection.GUEST_5G]
|
|
187
|
+
status.iot_2g_enable = wifi_status[Connection.IOT_2G]
|
|
188
|
+
status.iot_5g_enable = wifi_status[Connection.IOT_5G]
|
|
189
|
+
|
|
190
|
+
status.wired_total = sum(1 for device in mapped_devices if device.type == Connection.WIRED)
|
|
191
|
+
status.wifi_clients_total = sum(1 for device in mapped_devices
|
|
192
|
+
if device.type in (Connection.HOST_2G, Connection.HOST_5G))
|
|
193
|
+
status.guest_clients_total = sum(1 for device in mapped_devices
|
|
194
|
+
if device.type in (Connection.GUEST_2G, Connection.GUEST_5G))
|
|
195
|
+
status.iot_clients_total = sum(1 for device in mapped_devices
|
|
196
|
+
if device.type in (Connection.IOT_2G, Connection.IOT_5G))
|
|
197
|
+
status.clients_total = (status.wired_total + status.wifi_clients_total +
|
|
198
|
+
status.guest_clients_total + status.iot_clients_total)
|
|
199
|
+
|
|
200
|
+
status.devices = mapped_devices
|
|
201
|
+
return status
|
|
202
|
+
|
|
203
|
+
def reboot(self) -> None:
|
|
204
|
+
self.request(6, 1, True)
|
|
205
|
+
|
|
206
|
+
def set_wifi(self, wifi: Connection, enable: bool) -> None:
|
|
207
|
+
enable_string = f'bEnable {int(enable)}'
|
|
208
|
+
text = f'id {RouterConstants.CONNECTION_REQUESTS_MAP[wifi]}\r\n{enable_string}'
|
|
209
|
+
body = self._encrypt_body(text)
|
|
210
|
+
self.request(1, 0, True, data=body)
|
|
211
|
+
|
|
212
|
+
def get_ipv4_status(self) -> IPv4Status:
|
|
213
|
+
mac_info_request = "1|1,0,0"
|
|
214
|
+
lan_ip_request = "4|1,0,0"
|
|
215
|
+
dhcp_request = "8|1,0,0"
|
|
216
|
+
link_type_request = "22|1,0,0"
|
|
217
|
+
wan_ip_request = "23|1,0,0"
|
|
218
|
+
static_ip_request = "24|1,0,0"
|
|
219
|
+
all_requests = [
|
|
220
|
+
mac_info_request, lan_ip_request, dhcp_request, link_type_request, wan_ip_request, static_ip_request]
|
|
221
|
+
request_text = '#'.join(all_requests)
|
|
222
|
+
body = self._encrypt_body(request_text)
|
|
223
|
+
|
|
224
|
+
response = self.request(2, 1, True, data=body)
|
|
225
|
+
response_text = self._decrypt_data(response.text)
|
|
226
|
+
|
|
227
|
+
matches = TplinkC80Router.DATA_REGEX.findall(response_text)
|
|
228
|
+
|
|
229
|
+
data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
|
|
230
|
+
|
|
231
|
+
network_info = {
|
|
232
|
+
'lan_mac': self._extract_value(data_blocks[mac_info_request], "mac 0 "),
|
|
233
|
+
'wan_mac': self._extract_value(data_blocks[mac_info_request], "mac 1 "),
|
|
234
|
+
'lan_ip': self._extract_value(data_blocks[lan_ip_request], "ip "),
|
|
235
|
+
'wan_ip': self._extract_value(data_blocks[wan_ip_request], "ip "),
|
|
236
|
+
'gateway_ip': self._extract_value(data_blocks[wan_ip_request], "gateway "),
|
|
237
|
+
'uptime': self._extract_value(data_blocks[wan_ip_request], "upTime "),
|
|
238
|
+
'wan_mask': self._extract_value(data_blocks[wan_ip_request], "mask "),
|
|
239
|
+
'lan_mask': self._extract_value(data_blocks[lan_ip_request], "mask "),
|
|
240
|
+
'dns_1': self._extract_value(data_blocks[wan_ip_request], "dns 0 "),
|
|
241
|
+
'dns_2': self._extract_value(data_blocks[wan_ip_request], "dns 1 "),
|
|
242
|
+
'dhcp_enabled': self._extract_value(data_blocks[dhcp_request], "enable "),
|
|
243
|
+
'link_type': self._extract_value(data_blocks[link_type_request], "linkType "),
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
ipv4status = IPv4Status()
|
|
247
|
+
ipv4status._wan_macaddr = EUI48(network_info['wan_mac'])
|
|
248
|
+
ipv4status._wan_ipv4_ipaddr = IPv4Address(network_info['wan_ip'])
|
|
249
|
+
ipv4status._wan_ipv4_gateway = IPv4Address(network_info['gateway_ip'])
|
|
250
|
+
ipv4status._wan_ipv4_conntype = RouterConstants.CONNECTION_TYPE_MAP[network_info['link_type']]
|
|
251
|
+
ipv4status._wan_ipv4_netmask = IPv4Address(network_info['wan_mask'])
|
|
252
|
+
ipv4status._wan_ipv4_pridns = IPv4Address(network_info['dns_1'])
|
|
253
|
+
ipv4status._wan_ipv4_snddns = IPv4Address(network_info['dns_2'])
|
|
254
|
+
ipv4status._lan_macaddr = EUI48(network_info['lan_mac'])
|
|
255
|
+
ipv4status._lan_ipv4_ipaddr = IPv4Address(network_info['lan_ip'])
|
|
256
|
+
ipv4status.lan_ipv4_dhcp_enable = network_info['dhcp_enabled'] == '1'
|
|
257
|
+
ipv4status._lan_ipv4_netmask = IPv4Address(network_info['lan_mask'])
|
|
258
|
+
return ipv4status
|
|
259
|
+
|
|
260
|
+
def get_ipv4_reservations(self) -> list[IPv4Reservation]:
|
|
261
|
+
body = self._encrypt_body('12|1,0,0')
|
|
262
|
+
|
|
263
|
+
response = self.request(2, 1, True, data=body)
|
|
264
|
+
response_text = self._decrypt_data(response.text)
|
|
265
|
+
matches = TplinkC80Router.DATA_REGEX.findall(response_text)
|
|
266
|
+
|
|
267
|
+
data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
|
|
268
|
+
filtered_reservations = self._parse_response_to_dict(data_blocks['12|1,0,0'])
|
|
269
|
+
|
|
270
|
+
mapped_reservations: list[IPv4Reservation] = []
|
|
271
|
+
for reservation in filtered_reservations:
|
|
272
|
+
reservation_to_add = IPv4Reservation(EUI48(reservation['mac']), IPv4Address(reservation['ip']),
|
|
273
|
+
reservation['name'], reservation['dhcpsEnable'] == '1')
|
|
274
|
+
mapped_reservations.append(reservation_to_add)
|
|
275
|
+
return mapped_reservations
|
|
276
|
+
|
|
277
|
+
def get_dhcp_leases(self) -> list[IPv4DHCPLease]:
|
|
278
|
+
body = self._encrypt_body('9|1,0,0')
|
|
279
|
+
|
|
280
|
+
response = self.request(2, 1, True, data=body)
|
|
281
|
+
response_text = self._decrypt_data(response.text)
|
|
282
|
+
matches = TplinkC80Router.DATA_REGEX.findall(response_text)
|
|
283
|
+
|
|
284
|
+
data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
|
|
285
|
+
|
|
286
|
+
filtered_leases = self._parse_response_to_dict(data_blocks['9|1,0,0'])
|
|
287
|
+
|
|
288
|
+
mapped_leases: list[IPv4DHCPLease] = []
|
|
289
|
+
for lease in filtered_leases:
|
|
290
|
+
lease_to_add = IPv4DHCPLease(EUI48(lease['mac']), IPv4Address(lease['ip']),
|
|
291
|
+
lease['hostName'], f'expires {lease["expires"]}')
|
|
292
|
+
mapped_leases.append(lease_to_add)
|
|
293
|
+
|
|
294
|
+
return mapped_leases
|
|
295
|
+
|
|
296
|
+
def get_vpn_status(self) -> VPNStatus:
|
|
297
|
+
body = self._encrypt_body("22|1,0,0")
|
|
298
|
+
|
|
299
|
+
response = self.request(2, 1, True, data=body)
|
|
300
|
+
response_text = self._decrypt_data(response.text)
|
|
301
|
+
matches = TplinkC80Router.DATA_REGEX.findall(response_text)
|
|
302
|
+
|
|
303
|
+
data_blocks = {match[0]: match[1].strip().split("\r\n") for match in matches}
|
|
304
|
+
|
|
305
|
+
vpn_status = VPNStatus()
|
|
306
|
+
vpn_status.pptpvpn_enable = self._extract_value(data_blocks["22|1,0,0"], "linkType ") == '4'
|
|
307
|
+
|
|
308
|
+
return vpn_status
|
|
309
|
+
|
|
310
|
+
def _parse_devices(self, device_data_response: list[str]) -> list[Device]:
|
|
311
|
+
filtered_devices = self._parse_response_to_dict(device_data_response)
|
|
312
|
+
|
|
313
|
+
device_type_to_connection = {
|
|
314
|
+
0: Connection.WIRED,
|
|
315
|
+
1: Connection.HOST_2G, 2: Connection.GUEST_2G,
|
|
316
|
+
3: Connection.HOST_5G, 4: Connection.GUEST_5G,
|
|
317
|
+
13: Connection.IOT_2G, 14: Connection.IOT_5G
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
mapped_devices = []
|
|
321
|
+
for device in filtered_devices:
|
|
322
|
+
if device['online'] == '1':
|
|
323
|
+
device_type = int(device['type'])
|
|
324
|
+
connection_type = device_type_to_connection.get(device_type, Connection.UNKNOWN)
|
|
325
|
+
else:
|
|
326
|
+
connection_type = Connection.UNKNOWN
|
|
327
|
+
|
|
328
|
+
device_to_add = Device(connection_type, EUI48(device['mac']), IPv4Address(device['ip']), device['name'])
|
|
329
|
+
device_to_add.up_speed = int(device['up'])
|
|
330
|
+
device_to_add.down_speed = int(device['down'])
|
|
331
|
+
mapped_devices.append(device_to_add)
|
|
332
|
+
return mapped_devices
|
|
333
|
+
|
|
334
|
+
def _parse_response_to_dict(self, response_data: list[str]) -> list[dict]:
|
|
335
|
+
result_dict = defaultdict(dict)
|
|
336
|
+
for entry in response_data:
|
|
337
|
+
parts = entry.split(' ', 2)
|
|
338
|
+
key, id_str = parts[0], parts[1]
|
|
339
|
+
value = parts[2] if len(parts) == 3 else ''
|
|
340
|
+
result_dict[int(id_str)][key] = value
|
|
341
|
+
|
|
342
|
+
return [v for _, v in result_dict.items() if v.get("ip") != "0.0.0.0"]
|
|
343
|
+
|
|
344
|
+
@staticmethod
|
|
345
|
+
def _encrypt_password(pwd: str, key: str = RouterConfig.KEY, encoding: str = RouterConfig.ENCODING) -> str:
|
|
346
|
+
max_len = max(len(key), len(pwd))
|
|
347
|
+
pwd = pwd.ljust(max_len, RouterConfig.PAD_CHAR)
|
|
348
|
+
key = key.ljust(max_len, RouterConfig.PAD_CHAR)
|
|
349
|
+
|
|
350
|
+
result = []
|
|
351
|
+
for i in range(max_len):
|
|
352
|
+
result.append(encoding[(ord(pwd[i]) ^ ord(key[i])) % len(encoding)])
|
|
353
|
+
|
|
354
|
+
return "".join(result)
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def _encode_token(encoded_password: str, response: str) -> str:
|
|
358
|
+
response_text = response.text.splitlines()
|
|
359
|
+
auth_info1 = response_text[RouterConstants.AUTH_TOKEN_INDEX1]
|
|
360
|
+
auth_info2 = response_text[RouterConstants.AUTH_TOKEN_INDEX2]
|
|
361
|
+
|
|
362
|
+
encoded_token = TplinkC80Router._encrypt_password(encoded_password, auth_info1, auth_info2)
|
|
363
|
+
return parse.quote(encoded_token, safe='!()*')
|
|
364
|
+
|
|
365
|
+
def _get_signature(self, datalen: int) -> str:
|
|
366
|
+
encryption = self._encryption
|
|
367
|
+
r = f'{encryption.aes_string}&s={str(int(encryption.seq) + datalen)}'
|
|
368
|
+
e = ''
|
|
369
|
+
n = 0
|
|
370
|
+
while n < len(r):
|
|
371
|
+
e += EncryptionWrapper.rsa_encrypt(r[n:53], encryption.nn_rsa, encryption.ee_rsa)
|
|
372
|
+
n += 53
|
|
373
|
+
return e
|
|
374
|
+
|
|
375
|
+
def _encrypt_body(self, text: str) -> str:
|
|
376
|
+
encryption = self._encryption
|
|
377
|
+
|
|
378
|
+
key_bytes = encryption.key_aes.encode("utf-8")
|
|
379
|
+
iv_bytes = encryption.iv_aes.encode("utf-8")
|
|
380
|
+
|
|
381
|
+
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
|
382
|
+
data = b64encode(cipher.encrypt(pad(text.encode("utf-8"), AES.block_size))).decode()
|
|
383
|
+
|
|
384
|
+
sign = self._get_signature(len(data))
|
|
385
|
+
return f'sign={sign}\r\ndata={data}'
|
|
386
|
+
|
|
387
|
+
def _decrypt_data(self, encrypted_text: str) -> str:
|
|
388
|
+
key_bytes = self._encryption.key_aes.encode("utf-8")
|
|
389
|
+
iv_bytes = self._encryption.iv_aes.encode("utf-8")
|
|
390
|
+
|
|
391
|
+
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
|
392
|
+
decrypted_padded = cipher.decrypt(b64decode(encrypted_text))
|
|
393
|
+
return unpad(decrypted_padded, AES.block_size).decode("utf-8")
|
|
394
|
+
|
|
395
|
+
def _extract_value(self, response_list, prefix):
|
|
396
|
+
return next((s.split(prefix, 1)[1] for s in response_list if s.startswith(prefix)), None)
|
|
397
|
+
|
|
398
|
+
def request(self, code: int, asyn: int, use_token: bool = False, data: str = None):
|
|
399
|
+
url = f"{self.host}/?code={code}&asyn={asyn}"
|
|
400
|
+
if use_token:
|
|
401
|
+
url += f"&id={self._encryption.token}"
|
|
402
|
+
try:
|
|
403
|
+
response = self._session.post(url, data=data, timeout=self.timeout, verify=self._verify_ssl)
|
|
404
|
+
# Raises exception for 4XX/5XX status codes for all requests except 1st in authorize
|
|
405
|
+
if not (code == 2 and asyn == 1 and use_token is False and data is None):
|
|
406
|
+
response.raise_for_status()
|
|
407
|
+
return response
|
|
408
|
+
except requests.exceptions.RequestException as e:
|
|
409
|
+
self._logger.error(f"Network error: {e}")
|
|
410
|
+
raise ClientException(f"Network error: {str(e)}") from e
|
tplinkrouterc6u/client/ex.py
CHANGED
|
@@ -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:
|
tplinkrouterc6u/client/mr.py
CHANGED
|
@@ -77,6 +77,8 @@ class TPLinkMRClientBase(AbstractRouter):
|
|
|
77
77
|
super().__init__(host, password, username, logger, verify_ssl, timeout)
|
|
78
78
|
|
|
79
79
|
self.req = Session()
|
|
80
|
+
if self._verify_ssl is False:
|
|
81
|
+
self.req.verify = False
|
|
80
82
|
self._token = None
|
|
81
83
|
self._hash = md5(f"{self.username}{self.password}".encode()).hexdigest()
|
|
82
84
|
self._nn = None
|
tplinkrouterc6u/client/xdr.py
CHANGED
|
@@ -22,6 +22,8 @@ class TPLinkXDRClient(AbstractRouter):
|
|
|
22
22
|
verify_ssl: bool = True, timeout: int = 30) -> None:
|
|
23
23
|
super().__init__(host, password, username, logger, verify_ssl, timeout)
|
|
24
24
|
self._session = Session()
|
|
25
|
+
if self._verify_ssl is False:
|
|
26
|
+
self._session.verify = False
|
|
25
27
|
|
|
26
28
|
def supports(self) -> bool:
|
|
27
29
|
response = self._session.get(self.host, timeout=self.timeout, verify=self._verify_ssl)
|
tplinkrouterc6u/provider.py
CHANGED
|
@@ -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,
|
|
21
|
-
|
|
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,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: tplinkrouterc6u
|
|
3
|
-
Version: 5.
|
|
4
|
-
Summary: TP-Link Router API
|
|
3
|
+
Version: 5.6.1
|
|
4
|
+
Summary: TP-Link Router API (supports also Mercusys Router)
|
|
5
5
|
Home-page: https://github.com/AlexandrErohin/TP-Link-Archer-C6U
|
|
6
6
|
Author: Alex Erohin
|
|
7
7
|
Author-email: alexanderErohin@yandex.ru
|
|
@@ -29,8 +29,8 @@ Dynamic: requires-dist
|
|
|
29
29
|
Dynamic: requires-python
|
|
30
30
|
Dynamic: summary
|
|
31
31
|
|
|
32
|
-
# TP-Link Router API
|
|
33
|
-
Python package for API access and management for TP-Link Routers. See [Supported routers](#supports)
|
|
32
|
+
# TP-Link Router API (supports also Mercusys Router)
|
|
33
|
+
Python package for API access and management for TP-Link and Mercusys Routers. See [Supported routers](#supports)
|
|
34
34
|
|
|
35
35
|
[](https://pypi.org/project/tplinkrouterc6u/)
|
|
36
36
|
[](https://pypi.org/project/tplinkrouterc6u/)
|
|
@@ -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
|
|
@@ -283,8 +284,11 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
283
284
|
- VPN.PPTP_VPN
|
|
284
285
|
|
|
285
286
|
## <a id="supports">Supported routers</a>
|
|
286
|
-
|
|
287
|
+
- [TP-LINK routers](#tplink)
|
|
288
|
+
- [MERCUSYS routers](#mercusys)
|
|
289
|
+
### <a id="tplink">TP-LINK routers</a>
|
|
287
290
|
- Archer A7 V5
|
|
291
|
+
- Archer A8 1.0
|
|
288
292
|
- Archer A9 V6
|
|
289
293
|
- Archer AX10 v1.0
|
|
290
294
|
- Archer AX12 v1.0
|
|
@@ -310,9 +314,11 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
310
314
|
- Archer BE3600 1.6
|
|
311
315
|
- Archer C1200 (v1.0, v2.0)
|
|
312
316
|
- Archer C2300 (v1.0, v2.0)
|
|
313
|
-
- Archer C6 (v2.0, v3.0)
|
|
317
|
+
- Archer C6 (v2.0, v3.0, 4.0)
|
|
314
318
|
- Archer C6U v1.0
|
|
315
319
|
- Archer C7 (v4.0, v5.0)
|
|
320
|
+
- Archer C60 v2.0
|
|
321
|
+
- Archer C80 (1.0, 2.20)
|
|
316
322
|
- Archer C5400X V1
|
|
317
323
|
- Archer GX90 v1.0
|
|
318
324
|
- Archer MR200 (v5, v5.3, v6.0)
|
|
@@ -334,6 +340,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
334
340
|
- Deco X60 V3
|
|
335
341
|
- Deco X90
|
|
336
342
|
- Deco XE75 (v1.0, v2.0)
|
|
343
|
+
- Deco XE75PRO (v3.0)
|
|
337
344
|
- EX511 v2.0
|
|
338
345
|
- HX510 v1.0
|
|
339
346
|
- NX510v v1.0
|
|
@@ -344,24 +351,12 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
344
351
|
- TL-MR6500v
|
|
345
352
|
- TL-WA3001 v1.0
|
|
346
353
|
- TL-XDR3010 V2
|
|
354
|
+
- XC220-G3v v2.30
|
|
355
|
+
### <a id="mercusys">MERCUSYS routers</a>
|
|
356
|
+
- MR47BE v1.0
|
|
357
|
+
- H60XR 1.0
|
|
347
358
|
|
|
348
|
-
|
|
349
|
-
- AD7200 V2
|
|
350
|
-
- Archer A6 (V2 and V3)
|
|
351
|
-
- Archer A10 (V1 and V2)
|
|
352
|
-
- Archer A20 (V1, V3)
|
|
353
|
-
- Archer C8 (V3 and V4)
|
|
354
|
-
- Archer C9 (V4 and V5)
|
|
355
|
-
- Archer C59 V2
|
|
356
|
-
- Archer C90 V6
|
|
357
|
-
- Archer C900 V1
|
|
358
|
-
- Archer C1200 V3
|
|
359
|
-
- Archer C1900 V2
|
|
360
|
-
- Archer C4000 (V2 and V3)
|
|
361
|
-
- Archer C5400 V2
|
|
362
|
-
- TL-WR1043N V5
|
|
363
|
-
|
|
364
|
-
Please let me know if you have tested integration with one of this or other model. Open an issue with info about router's model, hardware and firmware versions.
|
|
359
|
+
Please let me know if you have tested integration with any other model. Open an issue with info about router's model, hardware and firmware versions.
|
|
365
360
|
|
|
366
361
|
## <a id="add_support">Adding Support For More Models</a>
|
|
367
362
|
Guidelines [CONTRIBUTING.md](https://github.com/AlexandrErohin/TP-Link-Archer-C6U/blob/master/CONTRIBUTING.md)
|
|
@@ -1,31 +1,32 @@
|
|
|
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=ZW4Ad-Y-o_IVNz1YPdrtq6At-_hujT1sMtOEOLkgu24,42081
|
|
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
|
|
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=
|
|
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
|
-
tplinkrouterc6u/client/
|
|
16
|
+
tplinkrouterc6u/client/c80.py,sha256=3Cy9FCYHZaGzTGAjsB2BdjxpzJmBoW6TMDTDaCyUDjI,18437
|
|
16
17
|
tplinkrouterc6u/client/deco.py,sha256=7uVd9wt3aJGShft7L520AzFpQ85xbgRWM-8uyrpxE8Y,8229
|
|
17
|
-
tplinkrouterc6u/client/ex.py,sha256=
|
|
18
|
-
tplinkrouterc6u/client/mr.py,sha256=
|
|
18
|
+
tplinkrouterc6u/client/ex.py,sha256=ZK08mlOts3fbBJh9NUrzBADP0N9LHgzOXCKY2tF9gG0,13111
|
|
19
|
+
tplinkrouterc6u/client/mr.py,sha256=kJ-meAgMPub6aKtzBEVI3CNNzsVab7pLkOlXU9dj030,26030
|
|
19
20
|
tplinkrouterc6u/client/vr.py,sha256=7Tbu0IrWtr4HHtyrnLFXEJi1QctzhilciL7agtwQ0R8,5025
|
|
20
|
-
tplinkrouterc6u/client/xdr.py,sha256=
|
|
21
|
+
tplinkrouterc6u/client/xdr.py,sha256=QaZ_5vCaf8BV_JEs3S2Nz-QDREBYHGh3OUWIVS-fefY,10406
|
|
21
22
|
tplinkrouterc6u/common/__init__.py,sha256=pCTvVZ9CAwgb7MxRnLx0y1rI0sTKSwT24FfxWfQXeTM,33
|
|
22
23
|
tplinkrouterc6u/common/dataclass.py,sha256=QQTGb9w4Yt3GNkISgrQhvWPMEEJOsgFFZ2MI1MIAPjw,7807
|
|
23
24
|
tplinkrouterc6u/common/encryption.py,sha256=4HelTxzN6esMfDZRBt3m8bwB9Nj_biKijnCnrGWPWKg,6228
|
|
24
25
|
tplinkrouterc6u/common/exception.py,sha256=_0G8ZvW5__CsGifHrsZeULdl8c6EUD071sDCQsQgrHY,140
|
|
25
26
|
tplinkrouterc6u/common/helper.py,sha256=23b04fk9HuVinrZXMCS5R1rmF8uZ7eM-Cdnp7Br9NR0,572
|
|
26
27
|
tplinkrouterc6u/common/package_enum.py,sha256=4ykL_2Pw0nDEIH_qR9UJlFF6stTgSfhPz32r8KT-sh8,1624
|
|
27
|
-
tplinkrouterc6u-5.
|
|
28
|
-
tplinkrouterc6u-5.
|
|
29
|
-
tplinkrouterc6u-5.
|
|
30
|
-
tplinkrouterc6u-5.
|
|
31
|
-
tplinkrouterc6u-5.
|
|
28
|
+
tplinkrouterc6u-5.6.1.dist-info/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
|
|
29
|
+
tplinkrouterc6u-5.6.1.dist-info/METADATA,sha256=yR4yQKnQL71GuYxBQYi36CBaPgVd6n-14U_ntiHD568,15302
|
|
30
|
+
tplinkrouterc6u-5.6.1.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
31
|
+
tplinkrouterc6u-5.6.1.dist-info/top_level.txt,sha256=1iSCCIueqgEkrTxtQ-jiHe99jAB10zqrVdBcwvNfe_M,21
|
|
32
|
+
tplinkrouterc6u-5.6.1.dist-info/RECORD,,
|
tplinkrouterc6u/client/c6v4.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from requests import post
|
|
2
|
-
from tplinkrouterc6u.common.package_enum import Connection
|
|
3
|
-
from tplinkrouterc6u.common.dataclass import Firmware, Status, IPv4Status
|
|
4
|
-
from tplinkrouterc6u.common.exception import ClientException
|
|
5
|
-
from tplinkrouterc6u.client_abstract import AbstractRouter
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TplinkC6V4Router(AbstractRouter):
|
|
9
|
-
def supports(self) -> bool:
|
|
10
|
-
url = '{}/?code=2&asyn=1'.format(self.host)
|
|
11
|
-
try:
|
|
12
|
-
response = post(url, timeout=self.timeout, verify=self._verify_ssl)
|
|
13
|
-
except BaseException:
|
|
14
|
-
return False
|
|
15
|
-
if response.status_code == 401 and response.text.startswith('00'):
|
|
16
|
-
raise ClientException(('Your router is not supported. Please add your router support to '
|
|
17
|
-
'https://github.com/AlexandrErohin/TP-Link-Archer-C6U '
|
|
18
|
-
'by implementing methods for TplinkC6V4Router class'
|
|
19
|
-
))
|
|
20
|
-
return False
|
|
21
|
-
|
|
22
|
-
def authorize(self) -> None:
|
|
23
|
-
raise ClientException('Not Implemented')
|
|
24
|
-
|
|
25
|
-
def logout(self) -> None:
|
|
26
|
-
raise ClientException('Not Implemented')
|
|
27
|
-
|
|
28
|
-
def get_firmware(self) -> Firmware:
|
|
29
|
-
raise ClientException('Not Implemented')
|
|
30
|
-
|
|
31
|
-
def get_status(self) -> Status:
|
|
32
|
-
raise ClientException('Not Implemented')
|
|
33
|
-
|
|
34
|
-
def get_ipv4_status(self) -> IPv4Status:
|
|
35
|
-
raise ClientException('Not Implemented')
|
|
36
|
-
|
|
37
|
-
def reboot(self) -> None:
|
|
38
|
-
raise ClientException('Not Implemented')
|
|
39
|
-
|
|
40
|
-
def set_wifi(self, wifi: Connection, enable: bool) -> None:
|
|
41
|
-
raise ClientException('Not Implemented')
|
|
File without changes
|
|
File without changes
|