tplinkrouterc6u 5.7.0__tar.gz → 5.8.0__tar.gz
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.
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/PKG-INFO +4 -1
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/README.md +3 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/setup.py +1 -1
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_c6u.py +39 -0
- tplinkrouterc6u-5.8.0/test/test_client_wdr.py +292 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/__init__.py +1 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/c6u.py +1 -1
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/c80.py +5 -2
- tplinkrouterc6u-5.8.0/tplinkrouterc6u/client/wdr.py +672 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/provider.py +2 -1
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/PKG-INFO +4 -1
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/SOURCES.txt +2 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/LICENSE +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/setup.cfg +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/__init__.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_c1200.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_c80.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_deco.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_ex.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_mr.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/test/test_client_xdr.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/__init__.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/c1200.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/c5400x.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/deco.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/ex.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/mr.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/vr.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client/xdr.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/client_abstract.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/__init__.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/dataclass.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/encryption.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/exception.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/helper.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u/common/package_enum.py +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/dependency_links.txt +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/requires.txt +0 -0
- {tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tplinkrouterc6u
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.8.0
|
|
4
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
|
|
@@ -289,6 +289,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
289
289
|
- [TP-LINK routers](#tplink)
|
|
290
290
|
- [MERCUSYS routers](#mercusys)
|
|
291
291
|
### <a id="tplink">TP-LINK routers</a>
|
|
292
|
+
- Archer A6 V2.0
|
|
292
293
|
- Archer A7 V5
|
|
293
294
|
- Archer A8 (1.0, 2.20)
|
|
294
295
|
- Archer A9 V6
|
|
@@ -311,6 +312,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
311
312
|
- Archer AX3000 V1
|
|
312
313
|
- Archer AX6000 V1
|
|
313
314
|
- Archer AX11000 V1
|
|
315
|
+
- Archer BE550 v1.0
|
|
314
316
|
- Archer BE800 v1.0
|
|
315
317
|
- Archer BE805 v1.0
|
|
316
318
|
- Archer BE3600 1.6
|
|
@@ -356,6 +358,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
356
358
|
- TL-WA1201 3.0
|
|
357
359
|
- TL-WA3001 v1.0
|
|
358
360
|
- TL-XDR3010 V2
|
|
361
|
+
- TL-WDR3600 V1
|
|
359
362
|
- XC220-G3v v2.30
|
|
360
363
|
### <a id="mercusys">MERCUSYS routers</a>
|
|
361
364
|
- MR47BE v1.0
|
|
@@ -257,6 +257,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
257
257
|
- [TP-LINK routers](#tplink)
|
|
258
258
|
- [MERCUSYS routers](#mercusys)
|
|
259
259
|
### <a id="tplink">TP-LINK routers</a>
|
|
260
|
+
- Archer A6 V2.0
|
|
260
261
|
- Archer A7 V5
|
|
261
262
|
- Archer A8 (1.0, 2.20)
|
|
262
263
|
- Archer A9 V6
|
|
@@ -279,6 +280,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
279
280
|
- Archer AX3000 V1
|
|
280
281
|
- Archer AX6000 V1
|
|
281
282
|
- Archer AX11000 V1
|
|
283
|
+
- Archer BE550 v1.0
|
|
282
284
|
- Archer BE800 v1.0
|
|
283
285
|
- Archer BE805 v1.0
|
|
284
286
|
- Archer BE3600 1.6
|
|
@@ -324,6 +326,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
324
326
|
- TL-WA1201 3.0
|
|
325
327
|
- TL-WA3001 v1.0
|
|
326
328
|
- TL-XDR3010 V2
|
|
329
|
+
- TL-WDR3600 V1
|
|
327
330
|
- XC220-G3v v2.30
|
|
328
331
|
### <a id="mercusys">MERCUSYS routers</a>
|
|
329
332
|
- MR47BE v1.0
|
|
@@ -729,6 +729,45 @@ class TestTPLinkClient(TestCase):
|
|
|
729
729
|
self.assertEqual(result.lan_ipv4_dhcp_enable, False)
|
|
730
730
|
self.assertEqual(result.remote, None)
|
|
731
731
|
|
|
732
|
+
def test_get_status_wan_macaddr_empty(self) -> None:
|
|
733
|
+
response_status = '''
|
|
734
|
+
{
|
|
735
|
+
"success": true,
|
|
736
|
+
"data": {
|
|
737
|
+
"lan_macaddr": "06:e6:97:9e:23:f5",
|
|
738
|
+
"wan_macaddr": "",
|
|
739
|
+
"wan_ipv4_ipaddr": "0.0.0.0",
|
|
740
|
+
"wan_ipv4_gateway": "0.0.0.0"
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
'''
|
|
744
|
+
response_stats = '''
|
|
745
|
+
{
|
|
746
|
+
"data": [],
|
|
747
|
+
"timeout": false,
|
|
748
|
+
"success": true,
|
|
749
|
+
"operator": "load"
|
|
750
|
+
}
|
|
751
|
+
'''
|
|
752
|
+
|
|
753
|
+
class TPLinkRouterTest(TplinkRouter):
|
|
754
|
+
def request(self, path: str, data: str,
|
|
755
|
+
ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
|
|
756
|
+
if path == 'admin/status?form=all&operation=read':
|
|
757
|
+
return loads(response_status)['data']
|
|
758
|
+
elif path == 'admin/wireless?form=statistics':
|
|
759
|
+
return loads(response_stats)['data']
|
|
760
|
+
raise ClientException()
|
|
761
|
+
|
|
762
|
+
client = TPLinkRouterTest('', '')
|
|
763
|
+
result = client.get_status()
|
|
764
|
+
|
|
765
|
+
self.assertIsInstance(result, Status)
|
|
766
|
+
self.assertEqual(result.wan_macaddr, None)
|
|
767
|
+
self.assertEqual(result.wan_ipv4_addr, '0.0.0.0')
|
|
768
|
+
self.assertEqual(result.wan_ipv4_gateway, '0.0.0.0')
|
|
769
|
+
self.assertEqual(result.lan_macaddr, '06-E6-97-9E-23-F5')
|
|
770
|
+
|
|
732
771
|
|
|
733
772
|
if __name__ == '__main__':
|
|
734
773
|
main()
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from unittest import main, TestCase
|
|
2
|
+
from ipaddress import IPv4Address
|
|
3
|
+
from macaddress import EUI48
|
|
4
|
+
from tplinkrouterc6u.common.dataclass import Firmware, Status
|
|
5
|
+
from tplinkrouterc6u.common.dataclass import IPv4Status, IPv4Reservation, IPv4DHCPLease
|
|
6
|
+
from tplinkrouterc6u import ClientError
|
|
7
|
+
from tplinkrouterc6u.client.wdr import TplinkWDRRouter
|
|
8
|
+
|
|
9
|
+
_NETWAN = (
|
|
10
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar wanTypeDetectInfoArray = '
|
|
11
|
+
+ "new Array(\n1, 0, 4500, \n0,0 );\n</SCRIPT>\n"
|
|
12
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar dhcpInf = new Array(\n1,\n'
|
|
13
|
+
+ '0,\n1,\n0,\n0,\n0,\n0,\n0,\n"",\n"",\n0,\n0,\n"",\n"192.168.0.129",\n"255.255.255.0",\n'
|
|
14
|
+
+ '"192.168.0.1",\n1,\n0,\n1500,\n0,\n"8.8.8.8",\n1,\n"8.8.4.4",\n0,\n0,\n0,\n"TL-WDR3600",\n0,0 );\n</SCRIPT>\n'
|
|
15
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar wantypeinfo = new Array(\n6,\n0,\n'
|
|
16
|
+
+ '"WanDynamicIpCfgRpm.htm",\n1,\n"WanStaticIpCfgRpm.htm",\n'
|
|
17
|
+
+ '2,\n"PPPoECfgRpm.htm",\n5,\n"BPACfgRpm.htm",\n6,\n"L2TPCfgRpm.htm",\n7,\n"PPTPCfgRpm.htm",\n0,0 );\n</SCRIPT>'
|
|
18
|
+
)
|
|
19
|
+
_NETLAN = (
|
|
20
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar lanPara = new Array(\n"C4-6E-1F-41-67-C0",\n'
|
|
21
|
+
+ '"192.168.1.254",\n2,\n"255.255.255.0",\n1,\n0,0 );\n</SCRIPT>'
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
_W24STA = (
|
|
25
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar wlanHostPara = new Array(\n1, 1, 8, 5000, 4,\n'
|
|
26
|
+
+ '0,0);\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\nvar hostList = new Array(\n'
|
|
27
|
+
+ '"D0-BD-53-57-3E-4A", 1, 170893827, 0,\n"08-16-AC-03-E2-FA", 1, 873409583, 0,\n"F8-F1-E8-CD-0A-CF", 1, '
|
|
28
|
+
+ '240958643, 0,\n"ED-49-92-1A-1D-D7", 1, 358743698, 0,\n"0E-50-99-5D-9A-D5", 1, 572346959, 0,\n0,0 );\n</SCRIPT>'
|
|
29
|
+
)
|
|
30
|
+
_W50STA = (
|
|
31
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar wlanHostPara = new Array(\n1, 1, 8, 5000, 4,\n'
|
|
32
|
+
+ '0,0 );\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\nvar hostList = new Array(\n'
|
|
33
|
+
+ '"50-A6-FC-6D-EB-D3", 1, 708938274, 0,\n"6A-D0-5F-2A-FA-2D", 1, 287340958, 0,\n"DA-69-2D-59-B3-FA", 1'
|
|
34
|
+
+ ", 540958641, 0,\n0,0 );\n</SCRIPT>"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_WLANGUEST = (
|
|
38
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar guestNetworkBandwidthInf = '
|
|
39
|
+
+ "new Array(\n"
|
|
40
|
+
+ '0,\n1000000,\n1000000,\n1024,\n1024,\n1,\n0,0);\n</SCRIPT>\n<SCRIPT language="javascript" '
|
|
41
|
+
+ 'type="text/javascript">'
|
|
42
|
+
+ '\nvar guestNetAccTime2gInf = new Array(\n1,\n1,\n0,\n0,\n0,\n1,\n0,\n0,\n0,\n0,\n0,\n0,\n0,\n1,\n"",\n"",\n'
|
|
43
|
+
+ "0,0 );"
|
|
44
|
+
+ '\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\nvar guestNetAccTime5gInf = new Array(\n'
|
|
45
|
+
+ '1,\n0,\n0,\n0,\n0,\n1,\n0,\n0,\n0,\n0,\n0,\n0,\n0,\n1,\n"",\n"",\n0,0 );\n</SCRIPT>\n'
|
|
46
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar guestNetworkInf = '
|
|
47
|
+
+ 'new Array(\n1, 1, 1, 0, "Pegasus", '
|
|
48
|
+
+ '"Pegasus", 1, 1, 3, 3, "333", "333", 0, 0, "p4ssw0rd", "p4ssw0rd", '
|
|
49
|
+
+ "0, 0, 0, 0, 3, 3, 1, 1, 5, 8, 0, \n0,0 );\n"
|
|
50
|
+
+ "</SCRIPT>"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
_DHCPCFG = (
|
|
54
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar DHCPPara = new Array(\n1,\n"192.168.1.129",\n'
|
|
55
|
+
+ '"192.168.1.192",\n120,\n"192.168.1.254",\n"internal.lan",\n"8.8.8.8",\n"8.8.4.4",\n0,\n0,0 );\n</SCRIPT>'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
_DHCPLEASES = (
|
|
59
|
+
'<SCRIPT language="javascript" type="text/javascript">var DHCPDynList = new Array(\n"aliquam",'
|
|
60
|
+
+ '"A9-A8-2B-F7-9F-5D","192.168.1.123","Permanent",\n"pharetra","B3-A5-1E-C3-92-A9","192.168.1.163","Permanent",\n'
|
|
61
|
+
+ '"ligula","71-34-47-FD-DE-84","192.168.1.165","Permanent",\n"vulputate","46-5F-5F-27-23-9F","192.168.1.103",'
|
|
62
|
+
+ '"Permanent",\n"amet","86-9F-53-91-04-2B","192.168.1.72","Permanent",\n"volutpat","FA-5C-6F-87-A3-5A",'
|
|
63
|
+
+ '"192.168.1.43",'
|
|
64
|
+
+ '"Permanent",\n"eget","C4-ED-6C-B6-F6-B9","192.168.1.112","Permanent",\n"ante","FF-C1-A3-93-C8-E6",'
|
|
65
|
+
+ '"192.168.1.38",'
|
|
66
|
+
+ '"Permanent",\n"pellentesque","E3-5E-59-2E-CF-AD","192.168.1.148","Permanent",\n"metus","AE-15-51-37-0E-9E",'
|
|
67
|
+
+ '"192.168.1.178","Permanent",\n0,0 );\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\n'
|
|
68
|
+
+ "var DHCPDynPara = new Array(\n10,\n4,\n0,0 );\n</SCRIPT>"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
_DHCPRESERVES = (
|
|
72
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar dhcpList = new Array(\n"9B-FA-04-D8-AB-8D",'
|
|
73
|
+
+ '"192.168.1.56",1,\n"EE-7C-6B-B6-05-2F","192.168.1.51",1,\n"09-51-B3-0B-92-01","192.168.1.21",1,\n'
|
|
74
|
+
+ '"49-6F-72-CD-68-5D","192.168.1.25",1,\n"DF-24-38-C1-FE-BB","192.168.1.51",1,\n"1A-08-6C-52-31-3D",'
|
|
75
|
+
+ '"192.168.1.22",1,\n"DE-E4-DF-9A-AD-0D","192.168.1.17",1,\n"DF-F2-CB-FE-46-15","192.168.1.26",1,\n'
|
|
76
|
+
+ '0,0 )\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">var DHCPStaticPara = '
|
|
77
|
+
+ "new Array(\n1,\n1,\n8,\n1,\n8,\n0,0 );\n</SCRIPT>"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
ABSTRACT_STATUS = (
|
|
81
|
+
'<SCRIPT language="javascript" type="text/javascript">\nvar statusPara = new Array(\n1,\n1,\n1,\n'
|
|
82
|
+
+ '22,\n20000,\n1468171,\n"3.13.34 Build 130909 Rel.53148n ",\n"WDR3600 v1 00000000",\n6732336,\n0, 0);\n'
|
|
83
|
+
+ '</SCRIPT>\n'
|
|
84
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar lanPara = new Array(\n"C4-6E-1F-41-67-C0", '
|
|
85
|
+
+ '"192.168.1.254", "255.255.255.0",\n0, 0);\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\n'
|
|
86
|
+
+ 'var wlanPara = new Array(\n1,\n"testSSID24",\n15,\n5,\n"C4-6E-1F-41-67-BF",\n"192.168.1.254",\n2,\n8,\n71,\n6,\n'
|
|
87
|
+
+ '6,\n0, 0);\n</SCRIPT>\n<SCRIPT language="javascript" type="text/javascript">\nvar wlan5GPara = new Array(\n1,\n'
|
|
88
|
+
+ '"testSSID",\n15,\n8,\n"C4-6E-1F-41-67-C0",\n"192.168.1.254",\n2,\n8,\n83,\n36,\n6,\n0, 0);\n</SCRIPT>\n'
|
|
89
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar statistList = '
|
|
90
|
+
+ "new Array(\n1129349328, 3900411475, 200068023, 165562287,\n0, 0);\n</SCRIPT>\n"
|
|
91
|
+
+ '<SCRIPT language="javascript" type="text/javascript">\nvar wanPara = new Array(\n4, '
|
|
92
|
+
+ '"C4-6E-1F-41-67-C1", "192.168.0.129", 1, "255.255.255.0", 0, 0, "192.168.0.1", 1, 1, 0, '
|
|
93
|
+
+ '"8.8.8.8 , 8.8.4.4", "", 0, 0, "0.0.0.0", "0.0.0.0", "0.0.0.0", "0.0.0.0 , 0.0.0.0", '
|
|
94
|
+
+ "0, 0, 0, 0, 0,\n0, 0);\n</SCRIPT>"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
ABSTRACT_NETWORK = {
|
|
98
|
+
"netWan": _NETWAN,
|
|
99
|
+
"netLan": _NETLAN,
|
|
100
|
+
"w24stations": _W24STA,
|
|
101
|
+
"w50stations": _W50STA,
|
|
102
|
+
"wgsettings": _WLANGUEST,
|
|
103
|
+
"dhcpconfig": _DHCPCFG,
|
|
104
|
+
"dhcplease": _DHCPLEASES,
|
|
105
|
+
"dhcpreserve": _DHCPRESERVES,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ResponseMock:
|
|
110
|
+
def __init__(self, text: str, status_code=0):
|
|
111
|
+
self.content = text.encode("utf8")
|
|
112
|
+
self.status_code = status_code
|
|
113
|
+
self.headers: dict = {}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class TplinkWDRRouterTest(TplinkWDRRouter):
|
|
117
|
+
response = ""
|
|
118
|
+
|
|
119
|
+
def request(
|
|
120
|
+
self,
|
|
121
|
+
section: str,
|
|
122
|
+
data: str,
|
|
123
|
+
ignore_response: bool = False,
|
|
124
|
+
ignore_errors: bool = False,
|
|
125
|
+
) -> str | dict | None:
|
|
126
|
+
# only a test, so no extra headers
|
|
127
|
+
# Responses
|
|
128
|
+
sections = "summary,status,"
|
|
129
|
+
sections += "netWan,netLan,dualBand,"
|
|
130
|
+
sections += "w24settings,w24wps,w24sec,w24adv,w24stations,"
|
|
131
|
+
sections += "w50settings,w50wps,w50sec,w50adv,w50stations,"
|
|
132
|
+
sections += "wgsettings,wgshare,dhcpconfig,dhcplease,dhcpreserve,"
|
|
133
|
+
sections += "portFwd,sysroute,upnpFwd"
|
|
134
|
+
|
|
135
|
+
section_list = sections.split(",")
|
|
136
|
+
|
|
137
|
+
if ignore_response:
|
|
138
|
+
return None
|
|
139
|
+
elif section == "check":
|
|
140
|
+
resp = ResponseMock("", 200)
|
|
141
|
+
resp.headers["www-authenticate"] = (
|
|
142
|
+
'Basic realm="TP-LINK Wireless Dual Band Gigabit Router WDR3600"'
|
|
143
|
+
)
|
|
144
|
+
return resp
|
|
145
|
+
elif section in section_list:
|
|
146
|
+
if section in ["summary", "status"]:
|
|
147
|
+
return ResponseMock(ABSTRACT_STATUS).content
|
|
148
|
+
elif section in ["w24stations", "w50stations", "dhcpreserve"]:
|
|
149
|
+
return ResponseMock(ABSTRACT_NETWORK[section]).content
|
|
150
|
+
elif section in [
|
|
151
|
+
"netLan",
|
|
152
|
+
"netWan",
|
|
153
|
+
# 'w24stations',
|
|
154
|
+
# 'w50stations',
|
|
155
|
+
"wgsettings",
|
|
156
|
+
"dhcpconfig",
|
|
157
|
+
"dhcplease",
|
|
158
|
+
# 'dhcpreserve',
|
|
159
|
+
]:
|
|
160
|
+
return ResponseMock(ABSTRACT_NETWORK[section]).content
|
|
161
|
+
else:
|
|
162
|
+
return ""
|
|
163
|
+
# raise ClientError (f'Section {section} not allowed')
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
error = ""
|
|
167
|
+
error = (
|
|
168
|
+
(
|
|
169
|
+
"WDRRouter - {} - Response with error; Request {} - Response {}".format(
|
|
170
|
+
self.__class__.__name__, section, data
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
if not error
|
|
174
|
+
else error
|
|
175
|
+
)
|
|
176
|
+
if self._logger:
|
|
177
|
+
self._logger.debug(error)
|
|
178
|
+
|
|
179
|
+
raise ClientError(error)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class TestTPLinkWDRClient(TestCase):
|
|
183
|
+
|
|
184
|
+
def test_supports(self) -> None:
|
|
185
|
+
client = TplinkWDRRouterTest("", "")
|
|
186
|
+
# client.response = ResponseMock(ABSTRACT_STATUS)
|
|
187
|
+
supports = client.supports()
|
|
188
|
+
self.assertTrue(supports)
|
|
189
|
+
|
|
190
|
+
def test_get_firmware(self) -> None:
|
|
191
|
+
|
|
192
|
+
client = TplinkWDRRouterTest("", "")
|
|
193
|
+
client.response = ResponseMock(ABSTRACT_STATUS)
|
|
194
|
+
firmware = client.get_firmware()
|
|
195
|
+
|
|
196
|
+
self.assertIsInstance(firmware, Firmware)
|
|
197
|
+
self.assertEqual(firmware.hardware_version, "WDR3600 v1 00000000")
|
|
198
|
+
self.assertEqual(firmware.model, "WDR3600")
|
|
199
|
+
self.assertEqual(
|
|
200
|
+
firmware.firmware_version.strip(), "3.13.34 Build 130909 Rel.53148n"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def test_get_ipv4(self) -> None:
|
|
204
|
+
|
|
205
|
+
client = TplinkWDRRouterTest("", "")
|
|
206
|
+
ipv4status: IPv4Status = IPv4Status()
|
|
207
|
+
ipv4status = client.get_ipv4_status()
|
|
208
|
+
|
|
209
|
+
self.assertIsInstance(ipv4status, IPv4Status)
|
|
210
|
+
self.assertEqual(ipv4status._wan_macaddr, EUI48("C4-6E-1F-41-67-C1"))
|
|
211
|
+
self.assertEqual(
|
|
212
|
+
IPv4Address(ipv4status.wan_ipv4_ipaddr), IPv4Address("192.168.0.129")
|
|
213
|
+
)
|
|
214
|
+
self.assertEqual(
|
|
215
|
+
IPv4Address(ipv4status.wan_ipv4_gateway), IPv4Address("192.168.0.1")
|
|
216
|
+
)
|
|
217
|
+
self.assertEqual(ipv4status.wan_ipv4_conntype, "Dynamic IP")
|
|
218
|
+
self.assertEqual(
|
|
219
|
+
IPv4Address(ipv4status.wan_ipv4_netmask), IPv4Address("255.255.255.0")
|
|
220
|
+
)
|
|
221
|
+
self.assertEqual(
|
|
222
|
+
IPv4Address(ipv4status.wan_ipv4_pridns), IPv4Address("0.0.0.0")
|
|
223
|
+
)
|
|
224
|
+
self.assertEqual(
|
|
225
|
+
IPv4Address(ipv4status.wan_ipv4_snddns), IPv4Address("0.0.0.0")
|
|
226
|
+
)
|
|
227
|
+
self.assertEqual(ipv4status._lan_macaddr, EUI48("C4-6E-1F-41-67-C0"))
|
|
228
|
+
self.assertEqual(
|
|
229
|
+
IPv4Address(ipv4status.lan_ipv4_ipaddr), IPv4Address("192.168.1.254")
|
|
230
|
+
)
|
|
231
|
+
self.assertEqual(ipv4status.lan_ipv4_dhcp_enable, True)
|
|
232
|
+
self.assertEqual(
|
|
233
|
+
IPv4Address(ipv4status.lan_ipv4_netmask), IPv4Address("255.255.255.0")
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def test_get_ipv4_reservations(self) -> None:
|
|
237
|
+
client = TplinkWDRRouterTest("", "")
|
|
238
|
+
ipv4_reservations: list[IPv4Reservation] = client.get_ipv4_reservations()
|
|
239
|
+
fRes: IPv4Reservation = ipv4_reservations[0]
|
|
240
|
+
|
|
241
|
+
self.assertIsInstance(fRes, IPv4Reservation)
|
|
242
|
+
self.assertEqual(EUI48(fRes.macaddress), EUI48("9B-FA-04-D8-AB-8D"))
|
|
243
|
+
self.assertEqual(IPv4Address(fRes.ipaddress), IPv4Address("192.168.1.56"))
|
|
244
|
+
self.assertEqual(fRes.enabled, True)
|
|
245
|
+
|
|
246
|
+
def test_get_ipv4_dhcp_leases(self) -> None:
|
|
247
|
+
client = TplinkWDRRouterTest("", "")
|
|
248
|
+
dhcp_leases: list[IPv4DHCPLease] = client.get_ipv4_dhcp_leases()
|
|
249
|
+
|
|
250
|
+
self.assertIsInstance(dhcp_leases[0], IPv4DHCPLease)
|
|
251
|
+
self.assertEqual(dhcp_leases[0].macaddress, EUI48("A9-A8-2B-F7-9F-5D"))
|
|
252
|
+
self.assertEqual(dhcp_leases[0].ipaddress, IPv4Address("192.168.1.123"))
|
|
253
|
+
self.assertEqual(dhcp_leases[0].hostname, "aliquam")
|
|
254
|
+
self.assertEqual(dhcp_leases[0].lease_time, "Permanent")
|
|
255
|
+
|
|
256
|
+
self.assertIsInstance(dhcp_leases[1], IPv4DHCPLease)
|
|
257
|
+
self.assertEqual(dhcp_leases[1].macaddress, EUI48("B3-A5-1E-C3-92-A9"))
|
|
258
|
+
self.assertEqual(dhcp_leases[1].ipaddress, IPv4Address("192.168.1.163"))
|
|
259
|
+
self.assertEqual(dhcp_leases[1].hostname, "pharetra")
|
|
260
|
+
self.assertEqual(dhcp_leases[1].lease_time, "Permanent")
|
|
261
|
+
|
|
262
|
+
def test_get_status(self) -> None:
|
|
263
|
+
client = TplinkWDRRouterTest("", "")
|
|
264
|
+
client.response = ResponseMock(ABSTRACT_STATUS)
|
|
265
|
+
|
|
266
|
+
status = client.get_status()
|
|
267
|
+
|
|
268
|
+
self.assertIsInstance(status, Status)
|
|
269
|
+
self.assertEqual(status.wan_macaddr, "C4-6E-1F-41-67-C1")
|
|
270
|
+
self.assertIsInstance(status.wan_macaddress, EUI48)
|
|
271
|
+
self.assertEqual(status.lan_macaddr, "C4-6E-1F-41-67-C0")
|
|
272
|
+
self.assertIsInstance(status.lan_macaddress, EUI48)
|
|
273
|
+
self.assertEqual(status.wan_ipv4_addr, "192.168.0.129")
|
|
274
|
+
self.assertIsInstance(status.lan_ipv4_address, IPv4Address)
|
|
275
|
+
self.assertEqual(status.lan_ipv4_addr, "192.168.1.254")
|
|
276
|
+
self.assertEqual(status.wan_ipv4_gateway, "192.168.0.1")
|
|
277
|
+
self.assertIsInstance(status.wan_ipv4_address, IPv4Address)
|
|
278
|
+
|
|
279
|
+
self.assertEqual(status.wired_total, 10)
|
|
280
|
+
self.assertEqual(status.wifi_clients_total, 8)
|
|
281
|
+
self.assertEqual(status.guest_clients_total, 0)
|
|
282
|
+
self.assertEqual(status.clients_total, 18)
|
|
283
|
+
|
|
284
|
+
self.assertTrue(status.guest_2g_enable)
|
|
285
|
+
self.assertFalse(status.guest_5g_enable)
|
|
286
|
+
self.assertTrue(status.wifi_2g_enable)
|
|
287
|
+
self.assertTrue(status.wifi_5g_enable)
|
|
288
|
+
self.assertEqual(status.wan_ipv4_uptime, 6732336)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
if __name__ == "__main__":
|
|
292
|
+
main()
|
|
@@ -8,6 +8,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
|
|
11
|
+
from tplinkrouterc6u.client.wdr import TplinkWDRRouter
|
|
11
12
|
from tplinkrouterc6u.provider import TplinkRouterProvider
|
|
12
13
|
from tplinkrouterc6u.common.package_enum import Connection, VPN
|
|
13
14
|
from tplinkrouterc6u.common.dataclass import (
|
|
@@ -275,7 +275,7 @@ class TplinkBaseRouter(AbstractRouter, TplinkRequest):
|
|
|
275
275
|
data = self.request('admin/status?form=all&operation=read', 'operation=read')
|
|
276
276
|
|
|
277
277
|
status = Status()
|
|
278
|
-
status._wan_macaddr = EUI48(data['wan_macaddr']) if 'wan_macaddr' in data else None
|
|
278
|
+
status._wan_macaddr = EUI48(data['wan_macaddr']) if 'wan_macaddr' in data and data['wan_macaddr'] else None
|
|
279
279
|
status._lan_macaddr = EUI48(data['lan_macaddr'])
|
|
280
280
|
status._wan_ipv4_addr = IPv4Address(data['wan_ipv4_ipaddr']) if 'wan_ipv4_ipaddr' in data else None
|
|
281
281
|
status._lan_ipv4_addr = IPv4Address(data['lan_ipv4_ipaddr']) if 'lan_ipv4_ipaddr' in data else None
|
|
@@ -82,8 +82,11 @@ class TplinkC80Router(AbstractRouter):
|
|
|
82
82
|
self._encryption = EncryptionState()
|
|
83
83
|
|
|
84
84
|
def supports(self) -> bool:
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
try:
|
|
86
|
+
response = self.request(2, 1, data='0|1,0,0')
|
|
87
|
+
return response.status_code == 200 and response.text.startswith('00000')
|
|
88
|
+
except Exception:
|
|
89
|
+
return False
|
|
87
90
|
|
|
88
91
|
def authorize(self) -> None:
|
|
89
92
|
encoded_password = TplinkC80Router._encrypt_password(self.password)
|
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
from ipaddress import IPv4Address
|
|
3
|
+
from requests import get, Response
|
|
4
|
+
from logging import Logger
|
|
5
|
+
from macaddress import EUI48
|
|
6
|
+
from tplinkrouterc6u.common.helper import get_ip
|
|
7
|
+
from tplinkrouterc6u.common.package_enum import Connection
|
|
8
|
+
from tplinkrouterc6u.common.exception import ClientError
|
|
9
|
+
from tplinkrouterc6u.common.dataclass import (
|
|
10
|
+
Firmware,
|
|
11
|
+
Status,
|
|
12
|
+
Device,
|
|
13
|
+
IPv4Reservation,
|
|
14
|
+
IPv4DHCPLease,
|
|
15
|
+
IPv4Status,
|
|
16
|
+
)
|
|
17
|
+
from tplinkrouterc6u.client_abstract import AbstractRouter
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from html.parser import HTMLParser
|
|
21
|
+
|
|
22
|
+
dataUrls = {
|
|
23
|
+
"check": "/StatusRpm.htm",
|
|
24
|
+
"summary": "/StatusRpm.htm",
|
|
25
|
+
"netWan": "/WanDynamicIpCfgRpm.htm?wan=0",
|
|
26
|
+
"netLan": "/NetworkCfgRpm.htm",
|
|
27
|
+
# 'macClone': "",
|
|
28
|
+
# WIFI
|
|
29
|
+
"dualBand": "/WlanBandRpm.htm",
|
|
30
|
+
# 2.4 Ghz"
|
|
31
|
+
"w24settings": "/WlanNetworkRpm.htm",
|
|
32
|
+
"w24wps": "/WpsCfgRpm.htm",
|
|
33
|
+
"w24sec": "/WlanSecurityRpm.htm",
|
|
34
|
+
"w24macflt": "/WlanMacFilterRpm.htm",
|
|
35
|
+
"w24adv": "/WlanAdvRpm.htm",
|
|
36
|
+
"w24stations": "/WlanStationRpm.htm?Page=1",
|
|
37
|
+
# 5.0 Ghz
|
|
38
|
+
"w50settings": "/WlanNetworkRpm_5g.htm",
|
|
39
|
+
"w50wps": "/WpsCfgRpm_5g.htm",
|
|
40
|
+
"w50sec": "/WlanSecurityRpm_5g.htm",
|
|
41
|
+
"w50macflt": "/WlanMacFilterRpm_5g.htm",
|
|
42
|
+
"w50adv": "/WlanAdvRpm_5g.htm",
|
|
43
|
+
"w50stations": "/WlanStationRpm_5g.htm?Page=1",
|
|
44
|
+
# Guest Network
|
|
45
|
+
"wgsettings": "/GuestNetWirelessCfgRpm.htm",
|
|
46
|
+
"wgshare": "/GuestNetUsbCfgRpm.htm",
|
|
47
|
+
# DHCP
|
|
48
|
+
"dhcpconfig": "/LanDhcpServerRpm.htm",
|
|
49
|
+
"dhcplease": "/AssignedIpAddrListRpm.htm",
|
|
50
|
+
"dhcpreserve": "/FixMapCfgRpm.htm",
|
|
51
|
+
# Referer
|
|
52
|
+
"defReferer": "/MenuRpm.htm",
|
|
53
|
+
# routing
|
|
54
|
+
"sysroute": "/SysRouteTableRpm.htm",
|
|
55
|
+
"portFwd": "/VirtualServerRpm.htm",
|
|
56
|
+
"upnpFwd": "/UpnpCfgRpm.htm",
|
|
57
|
+
# Reboot
|
|
58
|
+
"reboot": "/SysRebootHelpRpm.htm",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def defaultHeaders():
|
|
63
|
+
# default headers for all requests
|
|
64
|
+
return {
|
|
65
|
+
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
66
|
+
"User-Agent": "TP-Link Scrapper",
|
|
67
|
+
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
68
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class HostId:
|
|
74
|
+
def __init__(self, ipaddr: str, host: str) -> None:
|
|
75
|
+
self.ipaddr = ipaddr
|
|
76
|
+
self.host = host
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class NetInfo:
|
|
81
|
+
def __init__(self) -> None:
|
|
82
|
+
self.wlan24Gcfg = {}
|
|
83
|
+
self.wlan24Gsec = {}
|
|
84
|
+
self.wlan24Gadv = {}
|
|
85
|
+
self.wlan24Gcli: list[Device] = []
|
|
86
|
+
|
|
87
|
+
self.wlan50Gcfg = {}
|
|
88
|
+
self.wlan50Gsec = {}
|
|
89
|
+
self.wlan50Gadv = {}
|
|
90
|
+
self.wlan50Gcli: list[Device] = []
|
|
91
|
+
|
|
92
|
+
self.guest24Gcfg = {}
|
|
93
|
+
self.guest50Gcfg = {}
|
|
94
|
+
|
|
95
|
+
self.ipv4 = {}
|
|
96
|
+
self.routing = {}
|
|
97
|
+
self.fwd_static = {}
|
|
98
|
+
self.fwd_pnp = {}
|
|
99
|
+
self.dhcp_cfg = {}
|
|
100
|
+
self.security = {}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class muParser(HTMLParser):
|
|
104
|
+
def __init__(self, tag, convert_charrefs: bool = True):
|
|
105
|
+
super().__init__(convert_charrefs=convert_charrefs)
|
|
106
|
+
|
|
107
|
+
self.tag = tag
|
|
108
|
+
self.data: list = []
|
|
109
|
+
self.cTag = ""
|
|
110
|
+
self.cIdx = 0
|
|
111
|
+
self.cBlock = ""
|
|
112
|
+
|
|
113
|
+
def handle_starttag(self, tag, attrs):
|
|
114
|
+
if tag == self.tag:
|
|
115
|
+
self.cBlock = ""
|
|
116
|
+
self.cTag = tag
|
|
117
|
+
|
|
118
|
+
def handle_endtag(self, tag):
|
|
119
|
+
if tag == self.tag:
|
|
120
|
+
self.data.append(self.cBlock.strip("\r\n"))
|
|
121
|
+
self.cIdx += 1
|
|
122
|
+
self.cBlock = ""
|
|
123
|
+
self.cTag = ""
|
|
124
|
+
|
|
125
|
+
def handle_data(self, data):
|
|
126
|
+
if self.cTag == self.tag:
|
|
127
|
+
self.cBlock += data
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class WDRRequest:
|
|
131
|
+
host = ""
|
|
132
|
+
credentials = ""
|
|
133
|
+
timeout = 10
|
|
134
|
+
_logged = False
|
|
135
|
+
_verify_ssl = False
|
|
136
|
+
_logger = None
|
|
137
|
+
_headers_request = {}
|
|
138
|
+
|
|
139
|
+
def buildUrl(self, section: str):
|
|
140
|
+
return "{}/userRpm{}".format(self.host, dataUrls[section])
|
|
141
|
+
|
|
142
|
+
def request(
|
|
143
|
+
self,
|
|
144
|
+
section: str,
|
|
145
|
+
data: str,
|
|
146
|
+
ignore_response: bool = False,
|
|
147
|
+
ignore_errors: bool = False,
|
|
148
|
+
) -> str | dict | None:
|
|
149
|
+
if not self._headers_request:
|
|
150
|
+
self._headers_request = defaultHeaders()
|
|
151
|
+
|
|
152
|
+
# add xtra headers: User-Agent, Authorization and Referer
|
|
153
|
+
self._headers_request["Referer"] = self.buildUrl("defReferer")
|
|
154
|
+
self._headers_request["User-Agent"] = "TP-Link Scrapper"
|
|
155
|
+
self._headers_request["Authorization"] = "Basic {}".format(self.credentials)
|
|
156
|
+
|
|
157
|
+
path = dataUrls[section]
|
|
158
|
+
url = self.buildUrl(section)
|
|
159
|
+
if section == "reboot":
|
|
160
|
+
url = url + "?Reboot=Reboot"
|
|
161
|
+
|
|
162
|
+
# Always GET, so data always is a query
|
|
163
|
+
if data:
|
|
164
|
+
url = url + f"?{data}"
|
|
165
|
+
|
|
166
|
+
response = get(
|
|
167
|
+
url,
|
|
168
|
+
headers=self._headers_request,
|
|
169
|
+
timeout=self.timeout,
|
|
170
|
+
verify=self._verify_ssl,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
data = response.content # better than .text for later parsing
|
|
174
|
+
if response.ok:
|
|
175
|
+
if ignore_response:
|
|
176
|
+
return None
|
|
177
|
+
if section == "check":
|
|
178
|
+
return response
|
|
179
|
+
|
|
180
|
+
return data
|
|
181
|
+
else:
|
|
182
|
+
if ignore_errors:
|
|
183
|
+
return data
|
|
184
|
+
error = ""
|
|
185
|
+
error = (
|
|
186
|
+
(
|
|
187
|
+
"WDRRouter - {} - Response with error; Request {} - Response {}".format(
|
|
188
|
+
self.__class__.__name__, path, data
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
if not error
|
|
192
|
+
else error
|
|
193
|
+
)
|
|
194
|
+
if self._logger:
|
|
195
|
+
self._logger.debug(error)
|
|
196
|
+
raise ClientError(error)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class TplinkWDRRouter(AbstractRouter, WDRRequest):
|
|
200
|
+
# _smart_network = True
|
|
201
|
+
_perf_status = False
|
|
202
|
+
|
|
203
|
+
def __init__(
|
|
204
|
+
self,
|
|
205
|
+
host: str,
|
|
206
|
+
password: str,
|
|
207
|
+
username: str = "admin",
|
|
208
|
+
logger: Logger = None,
|
|
209
|
+
verify_ssl: bool = True,
|
|
210
|
+
timeout: int = 30,
|
|
211
|
+
) -> None:
|
|
212
|
+
super().__init__(host, password, username, logger, verify_ssl, timeout)
|
|
213
|
+
|
|
214
|
+
self.credentials = base64.b64encode(
|
|
215
|
+
bytes(f"{self.username}:{self.password}", "utf8")
|
|
216
|
+
).decode("utf8")
|
|
217
|
+
# device data
|
|
218
|
+
self.status: Status = Status() # {}
|
|
219
|
+
self.brand = "TP-Link"
|
|
220
|
+
self.firmware: Firmware = {}
|
|
221
|
+
self.hostname = ""
|
|
222
|
+
self.ipv4status: IPv4Status = IPv4Status()
|
|
223
|
+
self.network: NetInfo = NetInfo()
|
|
224
|
+
self.ipv4Reserves: list[IPv4Reservation] = []
|
|
225
|
+
self.dhcpLeases: list[IPv4DHCPLease] = []
|
|
226
|
+
self.connDevices: list[Device] = []
|
|
227
|
+
|
|
228
|
+
# N/A. WDR family has no session support , so no "logged" state
|
|
229
|
+
def authorize(self) -> None:
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
def logout(self) -> None:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
def supports(self) -> bool:
|
|
236
|
+
try:
|
|
237
|
+
response: Response = self.request("check", "")
|
|
238
|
+
return response.status_code == 200
|
|
239
|
+
except Exception:
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
def get_firmware(self) -> Firmware:
|
|
243
|
+
self._updateStatus()
|
|
244
|
+
return self.firmware
|
|
245
|
+
|
|
246
|
+
def get_status(self) -> Status:
|
|
247
|
+
self._updateStatus()
|
|
248
|
+
return self.status
|
|
249
|
+
|
|
250
|
+
def get_ipv4_status(self) -> IPv4Status:
|
|
251
|
+
self._updateStatus()
|
|
252
|
+
self._updateNet()
|
|
253
|
+
return self.ipv4status
|
|
254
|
+
|
|
255
|
+
def get_ipv4_reservations(self):
|
|
256
|
+
self._updateNet()
|
|
257
|
+
return self.ipv4Reserves
|
|
258
|
+
|
|
259
|
+
def get_ipv4_dhcp_leases(self):
|
|
260
|
+
self._updateNet()
|
|
261
|
+
return self.dhcpLeases
|
|
262
|
+
|
|
263
|
+
def get_clients(self):
|
|
264
|
+
self._updateNet()
|
|
265
|
+
return self.connDevices
|
|
266
|
+
|
|
267
|
+
def reboot(self) -> None:
|
|
268
|
+
self.request("reboot", "Reboot=Reboot", True)
|
|
269
|
+
|
|
270
|
+
def set_wifi(self, wifi: Connection, enable: bool) -> None:
|
|
271
|
+
# main wifi cannot be activated / deactivated via software. Only by the phisical button
|
|
272
|
+
# Guest wifi can, but saved changes won't activate until next reboot
|
|
273
|
+
if wifi == Connection.GUEST_2G:
|
|
274
|
+
section = "wgsettings"
|
|
275
|
+
query = "setNetworkMode=1"
|
|
276
|
+
if wifi == Connection.GUEST_2G:
|
|
277
|
+
section = "wgsettings"
|
|
278
|
+
query = "setNetworkMode_5G=1"
|
|
279
|
+
|
|
280
|
+
self.request(section, query, True)
|
|
281
|
+
|
|
282
|
+
def update(self, what: str = "") -> None:
|
|
283
|
+
if what == "":
|
|
284
|
+
return None
|
|
285
|
+
if what.lower() == "status":
|
|
286
|
+
return self._updateStatus()
|
|
287
|
+
if what.lower() == "firmware":
|
|
288
|
+
return self._updateStatus()
|
|
289
|
+
if what.lower() == "net":
|
|
290
|
+
return self._updateNet()
|
|
291
|
+
if what.lower() == "all":
|
|
292
|
+
self._updateStatus()
|
|
293
|
+
self._updateNet()
|
|
294
|
+
return None
|
|
295
|
+
|
|
296
|
+
def _updateStatus(self) -> None:
|
|
297
|
+
raw = self.request("summary", "")
|
|
298
|
+
self._parseSummary(raw)
|
|
299
|
+
self._updateNet()
|
|
300
|
+
|
|
301
|
+
def _updateNet(self) -> None:
|
|
302
|
+
sections = "netWan,netLan,dualBand,"
|
|
303
|
+
sections += "w24settings,w24wps,w24sec,w24adv,"
|
|
304
|
+
sections += "w50settings,w50wps,w50sec,w50adv,"
|
|
305
|
+
sections += "wgsettings,wgshare,dhcpconfig,dhcplease,"
|
|
306
|
+
sections += "sysroute,upnpFwd"
|
|
307
|
+
|
|
308
|
+
section_list = sections.split(",")
|
|
309
|
+
for section in section_list:
|
|
310
|
+
self._updateSection(section)
|
|
311
|
+
|
|
312
|
+
multiPage_list = "w24stations,w50stations,dhcpreserve,portFwd".split(",")
|
|
313
|
+
for section in multiPage_list:
|
|
314
|
+
self._updateMultiSection(section)
|
|
315
|
+
|
|
316
|
+
self._updateDevices()
|
|
317
|
+
|
|
318
|
+
def _updateDevices(self):
|
|
319
|
+
isWireless: list = []
|
|
320
|
+
w24s: list = self.network.wlan24Gcli
|
|
321
|
+
|
|
322
|
+
self.connDevices = []
|
|
323
|
+
for wl24 in w24s:
|
|
324
|
+
if wl24[0] not in isWireless:
|
|
325
|
+
_dev: HostId = self._findHostInLeases(wl24[0])
|
|
326
|
+
thisone = Device(Connection.HOST_2G, wl24[0], _dev.ipaddr, _dev.host)
|
|
327
|
+
thisone.packets_received = wl24[3]
|
|
328
|
+
thisone.packets_sent = wl24[2]
|
|
329
|
+
self.connDevices.append(thisone)
|
|
330
|
+
isWireless.append(wl24[0])
|
|
331
|
+
|
|
332
|
+
w50s = self.network.wlan50Gcli
|
|
333
|
+
for wl50 in w50s:
|
|
334
|
+
if wl50[0] not in isWireless:
|
|
335
|
+
_dev: HostId = self._findHostInLeases(wl50[0])
|
|
336
|
+
thisone = Device(Connection.HOST_5G, wl50[0], _dev.ipaddr, _dev.host)
|
|
337
|
+
thisone.packets_received = wl50[3]
|
|
338
|
+
thisone.packets_sent = wl50[2]
|
|
339
|
+
self.connDevices.append(thisone)
|
|
340
|
+
isWireless.append(wl50[0])
|
|
341
|
+
|
|
342
|
+
self.status.wifi_clients_total = len(isWireless)
|
|
343
|
+
|
|
344
|
+
connected: list[IPv4DHCPLease] = self.dhcpLeases
|
|
345
|
+
client: IPv4DHCPLease = {}
|
|
346
|
+
|
|
347
|
+
wired_speed = 1 * 1024 * 1024 * 1024
|
|
348
|
+
|
|
349
|
+
for client in connected:
|
|
350
|
+
if client.macaddr not in isWireless:
|
|
351
|
+
thisone = Device(
|
|
352
|
+
Connection.WIRED, client.macaddr, client.ipaddr, client.hostname
|
|
353
|
+
)
|
|
354
|
+
thisone.up_speed = wired_speed
|
|
355
|
+
thisone.down_speed = wired_speed
|
|
356
|
+
self.connDevices.append(thisone)
|
|
357
|
+
|
|
358
|
+
self.status.devices = self.connDevices
|
|
359
|
+
|
|
360
|
+
wifiCli = len(isWireless)
|
|
361
|
+
totalCli = len(self.connDevices)
|
|
362
|
+
wiredCli = totalCli - wifiCli
|
|
363
|
+
|
|
364
|
+
self.status.wifi_clients_total = wifiCli
|
|
365
|
+
self.status.guest_clients_total = 0
|
|
366
|
+
self.status.clients_total = totalCli
|
|
367
|
+
self.status.wired_total = wiredCli
|
|
368
|
+
|
|
369
|
+
def _updateSection(self, section: str) -> None:
|
|
370
|
+
raw = self.request(section, "")
|
|
371
|
+
data = self._parseRawHTML(raw)
|
|
372
|
+
self._parseSection(section, data)
|
|
373
|
+
|
|
374
|
+
def _updateMultiSection(self, section: str) -> None:
|
|
375
|
+
# For sections with potentially more than one page
|
|
376
|
+
if section == "w24stations" or section == "w50stations":
|
|
377
|
+
raw = self.request(section, "")
|
|
378
|
+
data = self._parseRawHTML(raw)
|
|
379
|
+
mainData = data["script0"]
|
|
380
|
+
listData: list = data["script1"]
|
|
381
|
+
numTotal = mainData[0] - (mainData[1] - 1) * mainData[2]
|
|
382
|
+
|
|
383
|
+
nextPage = False
|
|
384
|
+
if numTotal > mainData[2]:
|
|
385
|
+
numTotal = mainData[2]
|
|
386
|
+
nextPage = True
|
|
387
|
+
|
|
388
|
+
while nextPage:
|
|
389
|
+
query = "Page=+int(mainData[1]+1"
|
|
390
|
+
raw = self.request(section, query)
|
|
391
|
+
nextPage = False
|
|
392
|
+
data = self._parseRawHTML(raw)
|
|
393
|
+
mainData = data["script0"]
|
|
394
|
+
listData.extend(data["script1"])
|
|
395
|
+
numTotal = mainData[0] - (mainData[1] - 1) * mainData[2]
|
|
396
|
+
if numTotal > mainData[2]:
|
|
397
|
+
numTotal = mainData[2]
|
|
398
|
+
nextPage = True
|
|
399
|
+
|
|
400
|
+
self._parseSection(section, {"script0": mainData, "script1": listData})
|
|
401
|
+
|
|
402
|
+
elif section == "dhcpreserve":
|
|
403
|
+
raw = self.request(section, "")
|
|
404
|
+
data = self._parseRawHTML(raw)
|
|
405
|
+
currpage = int(data["script1"][0])
|
|
406
|
+
lastpage = int(data["script1"][3])
|
|
407
|
+
tmpData = {}
|
|
408
|
+
while currpage < lastpage:
|
|
409
|
+
query = f"Page={str(currpage + 1)}"
|
|
410
|
+
raw = self.request(section, query)
|
|
411
|
+
tmpData = self._parseRawHTML(raw)
|
|
412
|
+
|
|
413
|
+
tArr = tmpData["script0"]
|
|
414
|
+
for item in tArr:
|
|
415
|
+
data["script0"].append(item)
|
|
416
|
+
|
|
417
|
+
currpage = int(tmpData["script1"][0])
|
|
418
|
+
lastpage = int(tmpData["script1"][3])
|
|
419
|
+
data["script1"] = tmpData["script1"]
|
|
420
|
+
|
|
421
|
+
self._parseSection(section, data)
|
|
422
|
+
|
|
423
|
+
elif section == "portFwd":
|
|
424
|
+
# TODO
|
|
425
|
+
# self._parseSection(section, data)
|
|
426
|
+
pass
|
|
427
|
+
|
|
428
|
+
def _parseSection(self, section: str, data: dict) -> None:
|
|
429
|
+
|
|
430
|
+
if section == "netLan":
|
|
431
|
+
lanData = data["script0"]
|
|
432
|
+
self.ipv4status._lan_ipv4_netmask = IPv4Address(lanData[3])
|
|
433
|
+
self.network.ipv4["igmpProxy"] = lanData[4]
|
|
434
|
+
self.ipv4status.lan_ipv4_dhcp_enable = False
|
|
435
|
+
|
|
436
|
+
elif section == "netWan":
|
|
437
|
+
wanData = data["script1"]
|
|
438
|
+
connType = self._get_conn_type(int(wanData[0]) - 1)
|
|
439
|
+
if not connType:
|
|
440
|
+
connType = "unkown"
|
|
441
|
+
self.ipv4status._wan_ipv4_conntype = connType
|
|
442
|
+
self.status.conn_type = connType
|
|
443
|
+
|
|
444
|
+
self.ipv4status._wan_ipv4_netmask = IPv4Address(wanData[14] or "0.0.0.0")
|
|
445
|
+
dns = ["0.0.0.0", "0.0.0.0"]
|
|
446
|
+
# self.ipv4status._wan_ipv4_pridns = '0.0.0.0'
|
|
447
|
+
# self.ipv4status._wan_ipv4_snddns = '0.0.0.0'
|
|
448
|
+
if wanData[19] == "1":
|
|
449
|
+
dns[0] = wanData[20]
|
|
450
|
+
dns[1] = wanData[22]
|
|
451
|
+
|
|
452
|
+
self.ipv4status._wan_ipv4_pridns = IPv4Address(dns[0])
|
|
453
|
+
self.ipv4status._wan_ipv4_snddns = IPv4Address(dns[1])
|
|
454
|
+
|
|
455
|
+
self.hostname = wanData[26]
|
|
456
|
+
|
|
457
|
+
elif section == "dualBand":
|
|
458
|
+
# TODO
|
|
459
|
+
pass
|
|
460
|
+
elif section == "w24settings" or section == "w50settings":
|
|
461
|
+
# TODO
|
|
462
|
+
pass
|
|
463
|
+
elif section == "w24sec" or section == "w50sec":
|
|
464
|
+
# TODO
|
|
465
|
+
pass
|
|
466
|
+
elif section == "w24adv" or section == "w50adv":
|
|
467
|
+
# TODO
|
|
468
|
+
pass
|
|
469
|
+
elif section == "w24stations" or section == "w50stations":
|
|
470
|
+
listData = data["script1"]
|
|
471
|
+
if len(listData) > 3:
|
|
472
|
+
for i in range(0, len(listData), 4):
|
|
473
|
+
tmpcli = [
|
|
474
|
+
listData[i],
|
|
475
|
+
int(listData[i + 1]),
|
|
476
|
+
int(listData[i + 2]),
|
|
477
|
+
int(listData[i + 3]),
|
|
478
|
+
]
|
|
479
|
+
if section == "w24stations":
|
|
480
|
+
self.network.wlan24Gcli.append(tmpcli)
|
|
481
|
+
elif section == "w50stations":
|
|
482
|
+
self.network.wlan50Gcli.append(tmpcli)
|
|
483
|
+
elif section == "wgsettings":
|
|
484
|
+
guestData = data["script3"]
|
|
485
|
+
self.status.guest_2g_enable = bool(int(guestData[2]))
|
|
486
|
+
self.status.guest_5g_enable = bool(int(guestData[3]))
|
|
487
|
+
|
|
488
|
+
elif section == "wgshare":
|
|
489
|
+
# TODO
|
|
490
|
+
pass
|
|
491
|
+
elif section == "sysroute":
|
|
492
|
+
# TODO
|
|
493
|
+
pass
|
|
494
|
+
elif section == "portFwd":
|
|
495
|
+
# TODO
|
|
496
|
+
pass
|
|
497
|
+
elif section == "upnpFwd":
|
|
498
|
+
# TODO
|
|
499
|
+
pass
|
|
500
|
+
elif section == "dhcpconfig":
|
|
501
|
+
cfg = data["script0"]
|
|
502
|
+
if cfg[0] == 1:
|
|
503
|
+
self.ipv4status.lan_ipv4_dhcp_enable = True
|
|
504
|
+
oCfg = {}
|
|
505
|
+
oCfg["enabled"] = bool(int(cfg[0]))
|
|
506
|
+
oCfg["range_start"] = cfg[1]
|
|
507
|
+
oCfg["range_end"] = cfg[2]
|
|
508
|
+
oCfg["lease_time"] = int(cfg[3])
|
|
509
|
+
oCfg["gateway"] = cfg[4]
|
|
510
|
+
oCfg["domain"] = cfg[5] or None
|
|
511
|
+
oCfg["dns_pri"] = cfg[6] or None
|
|
512
|
+
oCfg["dns_sec"] = cfg[7] or None
|
|
513
|
+
self.network.dhcp_cfg = oCfg
|
|
514
|
+
elif section == "dhcpreserve":
|
|
515
|
+
item: IPv4Reservation = {}
|
|
516
|
+
self.ipv4Reserves = []
|
|
517
|
+
for i in range(0, len(data["script0"]), 3):
|
|
518
|
+
_dev: HostId = self._findHostInLeases(data["script0"][i])
|
|
519
|
+
item = IPv4Reservation(
|
|
520
|
+
data["script0"][i],
|
|
521
|
+
data["script0"][i + 1],
|
|
522
|
+
_dev.host,
|
|
523
|
+
bool(int(data["script0"][i + 2])),
|
|
524
|
+
)
|
|
525
|
+
self.ipv4Reserves.append(item)
|
|
526
|
+
elif section == "dhcplease":
|
|
527
|
+
self.dhcpLeases = []
|
|
528
|
+
for i in range(0, len(data["script0"]), 4):
|
|
529
|
+
item = IPv4DHCPLease(
|
|
530
|
+
EUI48(data["script0"][i + 1]),
|
|
531
|
+
IPv4Address(data["script0"][i + 2]),
|
|
532
|
+
data["script0"][i],
|
|
533
|
+
data["script0"][i + 3],
|
|
534
|
+
)
|
|
535
|
+
self.dhcpLeases.append(item)
|
|
536
|
+
elif section == "portFwd":
|
|
537
|
+
# TODO
|
|
538
|
+
pass
|
|
539
|
+
|
|
540
|
+
def _parseSummary(self, raw: str) -> None:
|
|
541
|
+
data = self._parseRawHTML(raw)
|
|
542
|
+
tFirm = data["script0"][6]
|
|
543
|
+
tHard = data["script0"][7]
|
|
544
|
+
# WDR3600 v1 00000000
|
|
545
|
+
tModel = tHard.split(" ")
|
|
546
|
+
self.firmware = Firmware(tHard, tModel[0], tFirm)
|
|
547
|
+
self.status = Status()
|
|
548
|
+
self.status.wan_ipv4_uptime = int(data["script0"][8])
|
|
549
|
+
self.status._lan_ipv4_addr = get_ip(data["script1"][1])
|
|
550
|
+
self.status._lan_macaddr = EUI48(data["script1"][0])
|
|
551
|
+
|
|
552
|
+
self.ipv4status._lan_ipv4_ipaddr = get_ip(data["script1"][1])
|
|
553
|
+
self.ipv4status._lan_macaddr = EUI48(data["script1"][0])
|
|
554
|
+
|
|
555
|
+
self.status._wan_macaddr = EUI48(data["script5"][1])
|
|
556
|
+
self.status._wan_ipv4_addr = get_ip(data["script5"][2])
|
|
557
|
+
self.status._wan_ipv4_gateway = get_ip(data["script5"][7])
|
|
558
|
+
|
|
559
|
+
self.ipv4status._wan_macaddr = EUI48(data["script5"][1])
|
|
560
|
+
self.ipv4status._wan_ipv4_ipaddr = get_ip(data["script5"][2])
|
|
561
|
+
self.ipv4status._wan_ipv4_netmask = ""
|
|
562
|
+
self.ipv4status._wan_ipv4_gateway = get_ip(data["script5"][7])
|
|
563
|
+
|
|
564
|
+
self.status.guest_2g_enable = None
|
|
565
|
+
self.status.guest_5g_enable = None
|
|
566
|
+
self.status.wifi_2g_enable = bool(int(data["script2"][0]))
|
|
567
|
+
self.status.wifi_5g_enable = bool(int(data["script3"][0]))
|
|
568
|
+
|
|
569
|
+
self.status.conn_type = "unknown"
|
|
570
|
+
self.status.devices = []
|
|
571
|
+
|
|
572
|
+
def _get_conn_type(self, n: int) -> str:
|
|
573
|
+
wantypeinfo = [
|
|
574
|
+
6,
|
|
575
|
+
0,
|
|
576
|
+
"WanDynamicIpCfgRpm.htm",
|
|
577
|
+
1,
|
|
578
|
+
"WanStaticIpCfgRpm.htm",
|
|
579
|
+
2,
|
|
580
|
+
"PPPoECfgRpm.htm",
|
|
581
|
+
5,
|
|
582
|
+
"BPACfgRpm.htm",
|
|
583
|
+
6,
|
|
584
|
+
"L2TPCfgRpm.htm",
|
|
585
|
+
7,
|
|
586
|
+
"PPTPCfgRpm.htm",
|
|
587
|
+
0,
|
|
588
|
+
0,
|
|
589
|
+
]
|
|
590
|
+
wantype_filtered = wantypeinfo[2 * n + 1]
|
|
591
|
+
|
|
592
|
+
wan_type: list = [
|
|
593
|
+
"Dynamic IP",
|
|
594
|
+
"Static IP",
|
|
595
|
+
"PPPoE/Russia PPPoE",
|
|
596
|
+
"802.1x DHCP",
|
|
597
|
+
"802.1x Static IP",
|
|
598
|
+
"BigPond Cable",
|
|
599
|
+
"L2TP/Russia L2TP",
|
|
600
|
+
"PPTP/Russia PPTP",
|
|
601
|
+
]
|
|
602
|
+
return wan_type[wantype_filtered]
|
|
603
|
+
|
|
604
|
+
def _findHostInLeases(self, macaddr: str) -> HostId:
|
|
605
|
+
arr = self.dhcpLeases
|
|
606
|
+
for lease in arr:
|
|
607
|
+
if lease.macaddr == macaddr:
|
|
608
|
+
return HostId(lease.ipaddr, lease.hostname)
|
|
609
|
+
|
|
610
|
+
return HostId("0.0.0.0", "-")
|
|
611
|
+
|
|
612
|
+
def _parseRawHTML(self, rawHTML: str) -> dict:
|
|
613
|
+
|
|
614
|
+
parser = muParser("script")
|
|
615
|
+
if not rawHTML:
|
|
616
|
+
return {}
|
|
617
|
+
parser.feed(rawHTML.decode("utf8", "ignore"))
|
|
618
|
+
|
|
619
|
+
all_scripts = parser.data
|
|
620
|
+
data = {}
|
|
621
|
+
count = 0
|
|
622
|
+
for script in all_scripts:
|
|
623
|
+
|
|
624
|
+
if script == "":
|
|
625
|
+
continue
|
|
626
|
+
|
|
627
|
+
if not str(script).startswith(("var")):
|
|
628
|
+
continue
|
|
629
|
+
|
|
630
|
+
oneLiner = self._parseDataBlock(script)
|
|
631
|
+
|
|
632
|
+
newArr = []
|
|
633
|
+
for item in oneLiner.split(","):
|
|
634
|
+
newVal = None
|
|
635
|
+
try:
|
|
636
|
+
newVal = int(item)
|
|
637
|
+
except Exception:
|
|
638
|
+
try:
|
|
639
|
+
newVal = float(item)
|
|
640
|
+
except Exception:
|
|
641
|
+
newVal = item
|
|
642
|
+
newArr.append(newVal)
|
|
643
|
+
|
|
644
|
+
data["script" + str(count)] = newArr
|
|
645
|
+
count += 1
|
|
646
|
+
|
|
647
|
+
return data
|
|
648
|
+
|
|
649
|
+
def _parseDataBlock(self, text) -> str:
|
|
650
|
+
lines = text.splitlines()
|
|
651
|
+
if len(lines) < 1:
|
|
652
|
+
return []
|
|
653
|
+
if lines[0] == "":
|
|
654
|
+
lines.pop(0) # delete first line if empty
|
|
655
|
+
if lines[-1] == "":
|
|
656
|
+
lines.pop() # delete last line if empty
|
|
657
|
+
lines.pop(0)
|
|
658
|
+
lines.pop()
|
|
659
|
+
result: str = ""
|
|
660
|
+
if len(lines) == 1:
|
|
661
|
+
result = lines[0].replace(", ", ",").replace('"', "")
|
|
662
|
+
else:
|
|
663
|
+
linesNew = []
|
|
664
|
+
for oneLine in lines:
|
|
665
|
+
oneLine = oneLine.replace(", ", ",").replace('"', "")
|
|
666
|
+
linesNew.append(oneLine)
|
|
667
|
+
lines = linesNew
|
|
668
|
+
result = "".join(lines)
|
|
669
|
+
|
|
670
|
+
if result.endswith(","):
|
|
671
|
+
result = result[:-1]
|
|
672
|
+
return result
|
|
@@ -11,6 +11,7 @@ from tplinkrouterc6u.client.c5400x import TplinkC5400XRouter
|
|
|
11
11
|
from tplinkrouterc6u.client.c1200 import TplinkC1200Router
|
|
12
12
|
from tplinkrouterc6u.client.c80 import TplinkC80Router
|
|
13
13
|
from tplinkrouterc6u.client.vr import TPLinkVRClient
|
|
14
|
+
from tplinkrouterc6u.client.wdr import TplinkWDRRouter
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class TplinkRouterProvider:
|
|
@@ -18,7 +19,7 @@ class TplinkRouterProvider:
|
|
|
18
19
|
def get_client(host: str, password: str, username: str = 'admin', logger: Logger = None,
|
|
19
20
|
verify_ssl: bool = True, timeout: int = 30) -> AbstractRouter:
|
|
20
21
|
for client in [TplinkC5400XRouter, TPLinkVRClient, TPLinkEXClient, TPLinkMRClient, TPLinkDecoClient,
|
|
21
|
-
TPLinkXDRClient, TplinkRouter, TplinkC80Router]:
|
|
22
|
+
TPLinkXDRClient, TplinkRouter, TplinkC80Router, TplinkWDRRouter]:
|
|
22
23
|
router = client(host, password, username, logger, verify_ssl, timeout)
|
|
23
24
|
if router.supports():
|
|
24
25
|
return router
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tplinkrouterc6u
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.8.0
|
|
4
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
|
|
@@ -289,6 +289,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
289
289
|
- [TP-LINK routers](#tplink)
|
|
290
290
|
- [MERCUSYS routers](#mercusys)
|
|
291
291
|
### <a id="tplink">TP-LINK routers</a>
|
|
292
|
+
- Archer A6 V2.0
|
|
292
293
|
- Archer A7 V5
|
|
293
294
|
- Archer A8 (1.0, 2.20)
|
|
294
295
|
- Archer A9 V6
|
|
@@ -311,6 +312,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
311
312
|
- Archer AX3000 V1
|
|
312
313
|
- Archer AX6000 V1
|
|
313
314
|
- Archer AX11000 V1
|
|
315
|
+
- Archer BE550 v1.0
|
|
314
316
|
- Archer BE800 v1.0
|
|
315
317
|
- Archer BE805 v1.0
|
|
316
318
|
- Archer BE3600 1.6
|
|
@@ -356,6 +358,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
|
|
|
356
358
|
- TL-WA1201 3.0
|
|
357
359
|
- TL-WA3001 v1.0
|
|
358
360
|
- TL-XDR3010 V2
|
|
361
|
+
- TL-WDR3600 V1
|
|
359
362
|
- XC220-G3v v2.30
|
|
360
363
|
### <a id="mercusys">MERCUSYS routers</a>
|
|
361
364
|
- MR47BE v1.0
|
|
@@ -8,6 +8,7 @@ test/test_client_c80.py
|
|
|
8
8
|
test/test_client_deco.py
|
|
9
9
|
test/test_client_ex.py
|
|
10
10
|
test/test_client_mr.py
|
|
11
|
+
test/test_client_wdr.py
|
|
11
12
|
test/test_client_xdr.py
|
|
12
13
|
tplinkrouterc6u/__init__.py
|
|
13
14
|
tplinkrouterc6u/client_abstract.py
|
|
@@ -26,6 +27,7 @@ tplinkrouterc6u/client/deco.py
|
|
|
26
27
|
tplinkrouterc6u/client/ex.py
|
|
27
28
|
tplinkrouterc6u/client/mr.py
|
|
28
29
|
tplinkrouterc6u/client/vr.py
|
|
30
|
+
tplinkrouterc6u/client/wdr.py
|
|
29
31
|
tplinkrouterc6u/client/xdr.py
|
|
30
32
|
tplinkrouterc6u/common/__init__.py
|
|
31
33
|
tplinkrouterc6u/common/dataclass.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tplinkrouterc6u-5.7.0 → tplinkrouterc6u-5.8.0}/tplinkrouterc6u.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|