tplinkrouterc6u 5.7.0__py3-none-any.whl → 5.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -82,8 +82,11 @@ class TplinkC80Router(AbstractRouter):
82
82
  self._encryption = EncryptionState()
83
83
 
84
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')
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