tplinkrouterc6u 5.4.1__py3-none-any.whl → 5.5.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.
test/test_client_c6u.py CHANGED
@@ -64,6 +64,7 @@ class TestTPLinkClient(TestCase):
64
64
  ],
65
65
  "guest_5g_psk_key": "",
66
66
  "cpu_usage": 0.28,
67
+ "conn_type": "1",
67
68
  "guest_2g_encryption": "none",
68
69
  "wireless_5g_encryption": "psk",
69
70
  "guest_5g_ssid": "TP-Link_Guest_21CC_5G",
@@ -244,6 +245,7 @@ class TestTPLinkClient(TestCase):
244
245
  self.assertEqual(status.wifi_5g_enable, True)
245
246
  self.assertEqual(status.wan_ipv4_uptime, None)
246
247
  self.assertEqual(status.mem_usage, 0.43)
248
+ self.assertEqual(status.conn_type, '1')
247
249
  self.assertEqual(status.cpu_usage, 0.28)
248
250
  self.assertEqual(len(status.devices), 5)
249
251
  self.assertIsInstance(status.devices[0], Device)
test/test_client_ex.py CHANGED
@@ -277,6 +277,35 @@ class TestTPLinkEXClient(TestCase):
277
277
  self.assertEqual(result.lan_ipv4_dhcp_enable, True)
278
278
  self.assertEqual(result.remote, None)
279
279
 
280
+ def test_get_ipv4_status_empty(self) -> None:
281
+
282
+ DEV2_ADT_LAN = ('{"data":[],"operation":"gl","oid":"DEV2_ADT_LAN","success":true}')
283
+ DEV2_ADT_WAN = ('{"data":[],"operation":"gl","oid":"DEV2_ADT_WAN","success":true}')
284
+
285
+ class TPLinkEXClientTest(TPLinkEXClient):
286
+ self._token = True
287
+
288
+ def _request(self, url, method='POST', data_str=None, encrypt=False):
289
+ if 'DEV2_ADT_LAN' in data_str:
290
+ return 200, DEV2_ADT_LAN
291
+ elif 'DEV2_ADT_WAN' in data_str:
292
+ return 200, DEV2_ADT_WAN
293
+ raise ClientException()
294
+
295
+ client = TPLinkEXClientTest('', '')
296
+ result = client.get_ipv4_status()
297
+
298
+ self.assertIsInstance(result, IPv4Status)
299
+ self.assertEqual(result.wan_ipv4_ipaddr, None)
300
+ self.assertEqual(result.wan_ipv4_gateway, None)
301
+ self.assertEqual(result.wan_ipv4_netmask, None)
302
+ self.assertEqual(result.wan_ipv4_conntype, '')
303
+ self.assertEqual(result.lan_macaddr, '00-00-00-00-00-00')
304
+ self.assertEqual(result.lan_ipv4_ipaddr, '0.0.0.0')
305
+ self.assertEqual(result.lan_ipv4_netmask, '0.0.0.0')
306
+ self.assertEqual(result.lan_ipv4_dhcp_enable, False)
307
+ self.assertEqual(result.remote, None)
308
+
280
309
  def test_get_ipv4_status_one_wlan(self) -> None:
281
310
 
282
311
  DEV2_ADT_LAN = ('{"data":[{"MACAddress":"bf:75:44:4c:dc:9e","IPAddress":"192.168.5.1",'
test/test_client_mr.py CHANGED
@@ -310,6 +310,7 @@ X_TP_TotalPacketsReceived=467
310
310
  self.assertEqual(status.wan_ipv4_addr, '192.168.30.55')
311
311
  self.assertEqual(status.lan_ipv4_addr, '192.168.4.1')
312
312
  self.assertEqual(status.wan_ipv4_gateway, '192.168.30.1')
313
+ self.assertEqual(status.conn_type, 'ipoe_1_d')
313
314
  self.assertEqual(status.wired_total, 0)
314
315
  self.assertEqual(status.wifi_clients_total, 1)
315
316
  self.assertEqual(status.guest_clients_total, 0)
@@ -618,6 +619,7 @@ DNSServers=7.7.7.7,2.2.2.2
618
619
 
619
620
  self.assertIsInstance(result, IPv4Status)
620
621
  self.assertEqual(result.lan_macaddr, '00-00-00-00-00-00')
622
+ self.assertEqual(result.wan_ipv4_conntype, '')
621
623
  self.assertEqual(result.lan_ipv4_ipaddr, '0.0.0.0')
622
624
  self.assertEqual(result.lan_ipv4_netmask, '0.0.0.0')
623
625
  self.assertEqual(result.lan_ipv4_dhcp_enable, False)
@@ -3,6 +3,7 @@ from tplinkrouterc6u.client.deco import TPLinkDecoClient
3
3
  from tplinkrouterc6u.client_abstract import AbstractRouter
4
4
  from tplinkrouterc6u.client.mr import TPLinkMRClient
5
5
  from tplinkrouterc6u.client.ex import TPLinkEXClient
6
+ from tplinkrouterc6u.client.vr import TPLinkVRClient
6
7
  from tplinkrouterc6u.client.c6v4 import TplinkC6V4Router
7
8
  from tplinkrouterc6u.client.c5400x import TplinkC5400XRouter
8
9
  from tplinkrouterc6u.client.c1200 import TplinkC1200Router
@@ -284,6 +284,7 @@ class TplinkBaseRouter(AbstractRouter, TplinkRequest):
284
284
  status.wan_ipv4_uptime = data.get('wan_ipv4_uptime')
285
285
  status.mem_usage = data.get('mem_usage')
286
286
  status.cpu_usage = data.get('cpu_usage')
287
+ status.conn_type = data.get('conn_type')
287
288
  status.wired_total = len(data.get('access_devices_wired', []))
288
289
  status.wifi_clients_total = len(data.get('access_devices_wireless_host', []))
289
290
  status.guest_clients_total = len(data.get('access_devices_wireless_guest', []))
@@ -366,7 +367,7 @@ class TplinkBaseRouter(AbstractRouter, TplinkRequest):
366
367
  ipv4_status._wan_macaddr = get_mac(data.get('wan_macaddr', '00:00:00:00:00:00'))
367
368
  ipv4_status._wan_ipv4_ipaddr = get_ip(data.get('wan_ipv4_ipaddr', '0.0.0.0'))
368
369
  ipv4_status._wan_ipv4_gateway = get_ip(data.get('wan_ipv4_gateway', '0.0.0.0'))
369
- ipv4_status.wan_ipv4_conntype = data.get('wan_ipv4_conntype', '')
370
+ ipv4_status._wan_ipv4_conntype = data.get('wan_ipv4_conntype', '')
370
371
  ipv4_status._wan_ipv4_netmask = get_ip(data.get('wan_ipv4_netmask', '0.0.0.0'))
371
372
  ipv4_status._wan_ipv4_pridns = get_ip(data.get('wan_ipv4_pridns', '0.0.0.0'))
372
373
  ipv4_status._wan_ipv4_snddns = get_ip(data.get('wan_ipv4_snddns', '0.0.0.0'))
@@ -1,6 +1,6 @@
1
1
  from requests import post
2
2
  from tplinkrouterc6u.common.package_enum import Connection
3
- from tplinkrouterc6u.common.dataclass import Firmware, Status
3
+ from tplinkrouterc6u.common.dataclass import Firmware, Status, IPv4Status
4
4
  from tplinkrouterc6u.common.exception import ClientException
5
5
  from tplinkrouterc6u.client_abstract import AbstractRouter
6
6
 
@@ -31,6 +31,9 @@ class TplinkC6V4Router(AbstractRouter):
31
31
  def get_status(self) -> Status:
32
32
  raise ClientException('Not Implemented')
33
33
 
34
+ def get_ipv4_status(self) -> IPv4Status:
35
+ raise ClientException('Not Implemented')
36
+
34
37
  def reboot(self) -> None:
35
38
  raise ClientException('Not Implemented')
36
39
 
@@ -132,7 +132,7 @@ class TPLinkDecoClient(TplinkEncryption, AbstractRouter):
132
132
  element = get_value(data, ['wan', 'ip_info', 'gateway'])
133
133
  ipv4_status._wan_ipv4_gateway = IPv4Address(element) if element else None
134
134
  element = get_value(data, ['wan', 'dial_type'])
135
- ipv4_status.wan_ipv4_conntype = element if element else ''
135
+ ipv4_status._wan_ipv4_conntype = element if element else ''
136
136
  element = get_value(data, ['wan', 'ip_info', 'mask'])
137
137
  ipv4_status._wan_ipv4_netmask = IPv4Address(element) if element else None
138
138
  element = get_value(data, ['wan', 'ip_info', 'dns1'])
@@ -5,6 +5,7 @@ from macaddress import EUI48
5
5
  from ipaddress import IPv4Address
6
6
  from logging import Logger
7
7
  from tplinkrouterc6u.common.package_enum import Connection, VPN
8
+ from tplinkrouterc6u.common.helper import get_ip, get_mac, get_value
8
9
  from tplinkrouterc6u.common.dataclass import (
9
10
  Firmware,
10
11
  Status,
@@ -48,7 +49,6 @@ class TPLinkEXClient(TPLinkMRClientBase):
48
49
  verify_ssl: bool = True, timeout: int = 30) -> None:
49
50
  super().__init__(host, password, username, logger, verify_ssl, timeout)
50
51
 
51
- self.username = 'user'
52
52
  self._url_rsa_key = 'cgi/getGDPRParm'
53
53
 
54
54
  def logout(self) -> None:
@@ -203,26 +203,26 @@ class TPLinkEXClient(TPLinkMRClientBase):
203
203
  ]
204
204
  _, values = self.req_act(acts)
205
205
 
206
- if values[0].__class__ == list:
206
+ if values[0].__class__ == list and len(values[0]) > 0:
207
207
  values[0] = values[0][0]
208
208
 
209
209
  ipv4_status = IPv4Status()
210
- ipv4_status._lan_macaddr = EUI48(values[0]['MACAddress'])
211
- ipv4_status._lan_ipv4_ipaddr = IPv4Address(values[0]['IPAddress'])
212
- ipv4_status._lan_ipv4_netmask = IPv4Address(values[0]['IPSubnetMask'])
213
- ipv4_status.lan_ipv4_dhcp_enable = bool(int(values[0]['DHCPv4Enable']))
210
+ ipv4_status._lan_macaddr = get_mac(get_value(values, [0, 'MACAddress'], '00:00:00:00:00:00'))
211
+ ipv4_status._lan_ipv4_ipaddr = get_ip(get_value(values, [0, 'IPAddress'], '0.0.0.0'))
212
+ ipv4_status._lan_ipv4_netmask = get_ip(get_value(values, [0, 'IPSubnetMask'], '0.0.0.0'))
213
+ ipv4_status.lan_ipv4_dhcp_enable = bool(int(get_value(values, [0, 'DHCPv4Enable'], '0')))
214
214
 
215
215
  for item in values[1]:
216
216
  if int(item['enable']) == 0 and values[1].__class__ == list:
217
217
  continue
218
- ipv4_status._wan_macaddr = EUI48(item['MACAddr'])
219
- ipv4_status._wan_ipv4_ipaddr = IPv4Address(item['connIPv4Address'])
220
- ipv4_status._wan_ipv4_gateway = IPv4Address(item['connIPv4Gateway'])
221
- ipv4_status.wan_ipv4_conntype = item['name']
222
- ipv4_status._wan_ipv4_netmask = IPv4Address(item['connIPv4SubnetMask'])
223
- dns = item['connIPv4DnsServer'].split(',')
224
- ipv4_status._wan_ipv4_pridns = IPv4Address(dns[0])
225
- ipv4_status._wan_ipv4_snddns = IPv4Address(dns[1])
218
+ ipv4_status._wan_macaddr = get_mac(get_value(item, ['MACAddr'], '00:00:00:00:00:00'))
219
+ ipv4_status._wan_ipv4_ipaddr = get_ip(get_value(item, ['connIPv4Address'], '0.0.0.0'))
220
+ ipv4_status._wan_ipv4_gateway = get_ip(get_value(item, ['connIPv4Gateway'], '0.0.0.0'))
221
+ ipv4_status._wan_ipv4_conntype = get_value(item, ['name'], '')
222
+ ipv4_status._wan_ipv4_netmask = get_ip(get_value(item, ['connIPv4SubnetMask'], '0.0.0.0'))
223
+ dns = get_value(item, ['connIPv4DnsServer'], '').split(',')
224
+ ipv4_status._wan_ipv4_pridns = get_ip(dns[0] if len(dns) > 0 else '0.0.0.0')
225
+ ipv4_status._wan_ipv4_snddns = get_ip(dns[1] if len(dns) > 1 else '0.0.0.0')
226
226
 
227
227
  return ipv4_status
228
228
 
@@ -7,7 +7,7 @@ from datetime import timedelta, datetime
7
7
  from macaddress import EUI48
8
8
  from ipaddress import IPv4Address
9
9
  from logging import Logger
10
- from tplinkrouterc6u.common.helper import get_ip, get_mac
10
+ from tplinkrouterc6u.common.helper import get_ip, get_mac, get_value
11
11
  from tplinkrouterc6u.common.encryption import EncryptionWrapperMR
12
12
  from tplinkrouterc6u.common.package_enum import Connection, VPN
13
13
  from tplinkrouterc6u.common.dataclass import (
@@ -114,6 +114,213 @@ class TPLinkMRClientBase(AbstractRouter):
114
114
  ]
115
115
  self.req_act(acts)
116
116
 
117
+ def get_firmware(self) -> Firmware:
118
+ acts = [
119
+ self.ActItem(self.ActItem.GET, 'IGD_DEV_INFO', attrs=[
120
+ 'hardwareVersion',
121
+ 'modelName',
122
+ 'softwareVersion'
123
+ ])
124
+ ]
125
+ _, values = self.req_act(acts)
126
+
127
+ firmware = Firmware(values.get('hardwareVersion', ''), values.get('modelName', ''),
128
+ values.get('softwareVersion', ''))
129
+
130
+ return firmware
131
+
132
+ def get_status(self) -> Status:
133
+ status = Status()
134
+ acts = [
135
+ self.ActItem(self.ActItem.GS, 'LAN_IP_INTF', attrs=['X_TP_MACAddress', 'IPInterfaceIPAddress']),
136
+ self.ActItem(self.ActItem.GS, 'WAN_IP_CONN',
137
+ attrs=['enable', 'MACAddress', 'externalIPAddress', 'defaultGateway', 'name']),
138
+ self.ActItem(self.ActItem.GL, 'LAN_WLAN', attrs=['enable', 'X_TP_Band']),
139
+ self.ActItem(self.ActItem.GL, 'LAN_WLAN_GUESTNET', attrs=['enable', 'name']),
140
+ self.ActItem(self.ActItem.GL, 'LAN_HOST_ENTRY', attrs=[
141
+ 'IPAddress',
142
+ 'MACAddress',
143
+ 'hostName',
144
+ 'X_TP_ConnType',
145
+ 'active',
146
+ ]),
147
+ self.ActItem(self.ActItem.GS, 'LAN_WLAN_ASSOC_DEV', attrs=[
148
+ 'associatedDeviceMACAddress',
149
+ 'X_TP_TotalPacketsSent',
150
+ 'X_TP_TotalPacketsReceived',
151
+ ]),
152
+ ]
153
+ _, values = self.req_act(acts)
154
+
155
+ if values['0'].__class__ == list:
156
+ values['0'] = values['0'][0]
157
+
158
+ status._lan_macaddr = EUI48(values['0']['X_TP_MACAddress'])
159
+ status._lan_ipv4_addr = IPv4Address(values['0']['IPInterfaceIPAddress'])
160
+
161
+ for item in self._to_list(values.get('1')):
162
+ if int(item['enable']) == 0 and values.get('1').__class__ == list:
163
+ continue
164
+ status._wan_macaddr = EUI48(item['MACAddress']) if item.get('MACAddress') else None
165
+ status._wan_ipv4_addr = IPv4Address(item['externalIPAddress'])
166
+ status._wan_ipv4_gateway = IPv4Address(item['defaultGateway'])
167
+ status.conn_type = item.get('name', '')
168
+
169
+ if values['2'].__class__ != list:
170
+ status.wifi_2g_enable = bool(int(values['2']['enable']))
171
+ else:
172
+ status.wifi_2g_enable = bool(int(values['2'][0]['enable']))
173
+ status.wifi_5g_enable = bool(int(values['2'][1]['enable']))
174
+
175
+ if values['3'].__class__ != list:
176
+ status.guest_2g_enable = bool(int(values['3']['enable']))
177
+ else:
178
+ status.guest_2g_enable = bool(int(values['3'][0]['enable']))
179
+ status.guest_5g_enable = bool(int(values['3'][1]['enable']))
180
+
181
+ devices = {}
182
+ for val in self._to_list(values.get('4')):
183
+ if int(val['active']) == 0:
184
+ continue
185
+ conn = self.CLIENT_TYPES.get(int(val['X_TP_ConnType']))
186
+ if conn is None:
187
+ continue
188
+ elif conn == Connection.WIRED:
189
+ status.wired_total += 1
190
+ elif conn.is_guest_wifi():
191
+ status.guest_clients_total += 1
192
+ elif conn.is_host_wifi():
193
+ status.wifi_clients_total += 1
194
+ devices[val['MACAddress']] = Device(conn,
195
+ EUI48(val['MACAddress']),
196
+ IPv4Address(val['IPAddress']),
197
+ val['hostName'])
198
+
199
+ for val in self._to_list(values.get('5')):
200
+ if val['associatedDeviceMACAddress'] not in devices:
201
+ status.wifi_clients_total += 1
202
+ devices[val['associatedDeviceMACAddress']] = Device(
203
+ Connection.HOST_2G,
204
+ EUI48(val['associatedDeviceMACAddress']),
205
+ IPv4Address('0.0.0.0'),
206
+ '')
207
+ devices[val['associatedDeviceMACAddress']].packets_sent = int(val['X_TP_TotalPacketsSent'])
208
+ devices[val['associatedDeviceMACAddress']].packets_received = int(val['X_TP_TotalPacketsReceived'])
209
+
210
+ status.devices = list(devices.values())
211
+ status.clients_total = status.wired_total + status.wifi_clients_total + status.guest_clients_total
212
+
213
+ return status
214
+
215
+ def get_ipv4_reservations(self) -> [IPv4Reservation]:
216
+ acts = [
217
+ self.ActItem(self.ActItem.GL, 'LAN_DHCP_STATIC_ADDR', attrs=['enable', 'chaddr', 'yiaddr']),
218
+ ]
219
+ _, values = self.req_act(acts)
220
+
221
+ ipv4_reservations = []
222
+ for item in self._to_list(values):
223
+ ipv4_reservations.append(
224
+ IPv4Reservation(
225
+ EUI48(item['chaddr']),
226
+ IPv4Address(item['yiaddr']),
227
+ '',
228
+ bool(int(item['enable']))
229
+ ))
230
+
231
+ return ipv4_reservations
232
+
233
+ def get_ipv4_dhcp_leases(self) -> [IPv4DHCPLease]:
234
+ acts = [
235
+ self.ActItem(self.ActItem.GL, 'LAN_HOST_ENTRY', attrs=['IPAddress', 'MACAddress', 'hostName',
236
+ 'leaseTimeRemaining']),
237
+ ]
238
+ _, values = self.req_act(acts)
239
+
240
+ dhcp_leases = []
241
+ for item in self._to_list(values):
242
+ lease_time = item['leaseTimeRemaining']
243
+ dhcp_leases.append(
244
+ IPv4DHCPLease(
245
+ EUI48(item['MACAddress']),
246
+ IPv4Address(item['IPAddress']),
247
+ item['hostName'],
248
+ str(timedelta(seconds=int(lease_time))) if lease_time.isdigit() else 'Permanent',
249
+ ))
250
+
251
+ return dhcp_leases
252
+
253
+ def get_ipv4_status(self) -> IPv4Status:
254
+ acts = [
255
+ self.ActItem(self.ActItem.GS, 'LAN_IP_INTF',
256
+ attrs=['X_TP_MACAddress', 'IPInterfaceIPAddress', 'IPInterfaceSubnetMask']),
257
+ self.ActItem(self.ActItem.GET, 'LAN_HOST_CFG', '1,0,0,0,0,0', attrs=['DHCPServerEnable']),
258
+ self.ActItem(self.ActItem.GS, 'WAN_IP_CONN',
259
+ attrs=['enable', 'MACAddress', 'externalIPAddress', 'defaultGateway', 'name', 'subnetMask',
260
+ 'DNSServers']),
261
+ ]
262
+ _, values = self.req_act(acts)
263
+
264
+ ipv4_status = IPv4Status()
265
+ ipv4_status._lan_macaddr = get_mac(get_value(values, ['0', 'X_TP_MACAddress'], '00:00:00:00:00:00'))
266
+ ipv4_status._lan_ipv4_ipaddr = get_ip(get_value(values, ['0', 'IPInterfaceIPAddress'], '0.0.0.0'))
267
+ ipv4_status._lan_ipv4_netmask = get_ip(get_value(values, ['0', 'IPInterfaceSubnetMask'], '0.0.0.0'))
268
+ ipv4_status.lan_ipv4_dhcp_enable = bool(int(get_value(values, ['1', 'DHCPServerEnable'], '0')))
269
+
270
+ for item in self._to_list(values.get('2')):
271
+ if int(item.get('enable', '0')) == 0 and values.get('2').__class__ == list:
272
+ continue
273
+ ipv4_status._wan_macaddr = get_mac(item.get('MACAddress', '00:00:00:00:00:00'))
274
+ ipv4_status._wan_ipv4_ipaddr = get_ip(item.get('externalIPAddress', '0.0.0.0'))
275
+ ipv4_status._wan_ipv4_gateway = get_ip(item.get('defaultGateway', '0.0.0.0'))
276
+ ipv4_status._wan_ipv4_conntype = item.get('name', '')
277
+ ipv4_status._wan_ipv4_netmask = get_ip(item.get('subnetMask', '0.0.0.0'))
278
+ dns = item.get('DNSServers', '').split(',')
279
+ ipv4_status._wan_ipv4_pridns = get_ip(dns[0] if len(dns) > 0 else '0.0.0.0')
280
+ ipv4_status._wan_ipv4_snddns = get_ip(dns[1] if len(dns) > 1 else '0.0.0.0')
281
+
282
+ return ipv4_status
283
+
284
+ def set_wifi(self, wifi: Connection, enable: bool) -> None:
285
+ acts = [
286
+ self.ActItem(
287
+ self.ActItem.SET,
288
+ 'LAN_WLAN' if wifi in [Connection.HOST_2G, Connection.HOST_5G] else 'LAN_WLAN_MSSIDENTRY',
289
+ self.WIFI_SET[wifi],
290
+ attrs=['enable={}'.format(int(enable))]),
291
+ ]
292
+ self.req_act(acts)
293
+
294
+ def get_vpn_status(self) -> VPNStatus:
295
+ status = VPNStatus()
296
+ acts = [
297
+ self.ActItem(self.ActItem.GET, 'OPENVPN', attrs=['enable']),
298
+ self.ActItem(self.ActItem.GET, 'PPTPVPN', attrs=['enable']),
299
+ self.ActItem(self.ActItem.GL, 'OVPN_CLIENT', attrs=['connAct']),
300
+ self.ActItem(self.ActItem.GL, 'PVPN_CLIENT', attrs=['connAct']),
301
+ ]
302
+ _, values = self.req_act(acts)
303
+
304
+ status.openvpn_enable = values['0']['enable'] == '1'
305
+ status.pptpvpn_enable = values['1']['enable'] == '1'
306
+
307
+ for item in values['2']:
308
+ if item['connAct'] == '1':
309
+ status.openvpn_clients_total += 1
310
+
311
+ for item in values['3']:
312
+ if item['connAct'] == '1':
313
+ status.pptpvpn_clients_total += 1
314
+
315
+ return status
316
+
317
+ def set_vpn(self, vpn: VPN, enable: bool) -> None:
318
+ acts = [
319
+ self.ActItem(self.ActItem.SET, vpn.value, attrs=['enable={}'.format(int(enable))])
320
+ ]
321
+
322
+ self.req_act(acts)
323
+
117
324
  def req_act(self, acts: list):
118
325
  '''
119
326
  Requests ACTs via the cgi_gdpr proxy
@@ -303,7 +510,7 @@ class TPLinkMRClientBase(AbstractRouter):
303
510
  self._logger.debug(error)
304
511
  raise ClientException(error)
305
512
 
306
- def _request(self, url, method='POST', data_str=None, encrypt=False):
513
+ def _request(self, url, method='POST', data_str=None, encrypt=False, is_login=False):
307
514
  '''
308
515
  Prepares and sends an HTTP request to the host
309
516
  - sets up the headers, handles token auth
@@ -325,7 +532,7 @@ class TPLinkMRClientBase(AbstractRouter):
325
532
 
326
533
  # encrypt request data if needed (for the /cgi_gdpr endpoint)
327
534
  if encrypt:
328
- sign, data = self._prepare_data(data_str, False)
535
+ sign, data = self._prepare_data(data_str, is_login)
329
536
  data = 'sign={}\r\ndata={}\r\n'.format(sign, data)
330
537
  else:
331
538
  data = data_str
@@ -396,182 +603,6 @@ class TPLinkMRClient(TPLinkMRClientBase):
396
603
  if ret_code == self.HTTP_RET_OK:
397
604
  self._token = None
398
605
 
399
- def get_firmware(self) -> Firmware:
400
- acts = [
401
- self.ActItem(self.ActItem.GET, 'IGD_DEV_INFO', attrs=[
402
- 'hardwareVersion',
403
- 'modelName',
404
- 'softwareVersion'
405
- ])
406
- ]
407
- _, values = self.req_act(acts)
408
-
409
- firmware = Firmware(values.get('hardwareVersion', ''), values.get('modelName', ''),
410
- values.get('softwareVersion', ''))
411
-
412
- return firmware
413
-
414
- def get_status(self) -> Status:
415
- status = Status()
416
- acts = [
417
- self.ActItem(self.ActItem.GS, 'LAN_IP_INTF', attrs=['X_TP_MACAddress', 'IPInterfaceIPAddress']),
418
- self.ActItem(self.ActItem.GS, 'WAN_IP_CONN',
419
- attrs=['enable', 'MACAddress', 'externalIPAddress', 'defaultGateway']),
420
- self.ActItem(self.ActItem.GL, 'LAN_WLAN', attrs=['enable', 'X_TP_Band']),
421
- self.ActItem(self.ActItem.GL, 'LAN_WLAN_GUESTNET', attrs=['enable', 'name']),
422
- self.ActItem(self.ActItem.GL, 'LAN_HOST_ENTRY', attrs=[
423
- 'IPAddress',
424
- 'MACAddress',
425
- 'hostName',
426
- 'X_TP_ConnType',
427
- 'active',
428
- ]),
429
- self.ActItem(self.ActItem.GS, 'LAN_WLAN_ASSOC_DEV', attrs=[
430
- 'associatedDeviceMACAddress',
431
- 'X_TP_TotalPacketsSent',
432
- 'X_TP_TotalPacketsReceived',
433
- ]),
434
- ]
435
- _, values = self.req_act(acts)
436
-
437
- if values['0'].__class__ == list:
438
- values['0'] = values['0'][0]
439
-
440
- status._lan_macaddr = EUI48(values['0']['X_TP_MACAddress'])
441
- status._lan_ipv4_addr = IPv4Address(values['0']['IPInterfaceIPAddress'])
442
-
443
- for item in self._to_list(values.get('1')):
444
- if int(item['enable']) == 0 and values.get('1').__class__ == list:
445
- continue
446
- status._wan_macaddr = EUI48(item['MACAddress']) if item.get('MACAddress') else None
447
- status._wan_ipv4_addr = IPv4Address(item['externalIPAddress'])
448
- status._wan_ipv4_gateway = IPv4Address(item['defaultGateway'])
449
-
450
- if values['2'].__class__ != list:
451
- status.wifi_2g_enable = bool(int(values['2']['enable']))
452
- else:
453
- status.wifi_2g_enable = bool(int(values['2'][0]['enable']))
454
- status.wifi_5g_enable = bool(int(values['2'][1]['enable']))
455
-
456
- if values['3'].__class__ != list:
457
- status.guest_2g_enable = bool(int(values['3']['enable']))
458
- else:
459
- status.guest_2g_enable = bool(int(values['3'][0]['enable']))
460
- status.guest_5g_enable = bool(int(values['3'][1]['enable']))
461
-
462
- devices = {}
463
- for val in self._to_list(values.get('4')):
464
- if int(val['active']) == 0:
465
- continue
466
- conn = self.CLIENT_TYPES.get(int(val['X_TP_ConnType']))
467
- if conn is None:
468
- continue
469
- elif conn == Connection.WIRED:
470
- status.wired_total += 1
471
- elif conn.is_guest_wifi():
472
- status.guest_clients_total += 1
473
- elif conn.is_host_wifi():
474
- status.wifi_clients_total += 1
475
- devices[val['MACAddress']] = Device(conn,
476
- EUI48(val['MACAddress']),
477
- IPv4Address(val['IPAddress']),
478
- val['hostName'])
479
-
480
- for val in self._to_list(values.get('5')):
481
- if val['associatedDeviceMACAddress'] not in devices:
482
- status.wifi_clients_total += 1
483
- devices[val['associatedDeviceMACAddress']] = Device(
484
- Connection.HOST_2G,
485
- EUI48(val['associatedDeviceMACAddress']),
486
- IPv4Address('0.0.0.0'),
487
- '')
488
- devices[val['associatedDeviceMACAddress']].packets_sent = int(val['X_TP_TotalPacketsSent'])
489
- devices[val['associatedDeviceMACAddress']].packets_received = int(val['X_TP_TotalPacketsReceived'])
490
-
491
- status.devices = list(devices.values())
492
- status.clients_total = status.wired_total + status.wifi_clients_total + status.guest_clients_total
493
-
494
- return status
495
-
496
- def get_ipv4_reservations(self) -> [IPv4Reservation]:
497
- acts = [
498
- self.ActItem(self.ActItem.GL, 'LAN_DHCP_STATIC_ADDR', attrs=['enable', 'chaddr', 'yiaddr']),
499
- ]
500
- _, values = self.req_act(acts)
501
-
502
- ipv4_reservations = []
503
- for item in self._to_list(values):
504
- ipv4_reservations.append(
505
- IPv4Reservation(
506
- EUI48(item['chaddr']),
507
- IPv4Address(item['yiaddr']),
508
- '',
509
- bool(int(item['enable']))
510
- ))
511
-
512
- return ipv4_reservations
513
-
514
- def get_ipv4_dhcp_leases(self) -> [IPv4DHCPLease]:
515
- acts = [
516
- self.ActItem(self.ActItem.GL, 'LAN_HOST_ENTRY', attrs=['IPAddress', 'MACAddress', 'hostName',
517
- 'leaseTimeRemaining']),
518
- ]
519
- _, values = self.req_act(acts)
520
-
521
- dhcp_leases = []
522
- for item in self._to_list(values):
523
- lease_time = item['leaseTimeRemaining']
524
- dhcp_leases.append(
525
- IPv4DHCPLease(
526
- EUI48(item['MACAddress']),
527
- IPv4Address(item['IPAddress']),
528
- item['hostName'],
529
- str(timedelta(seconds=int(lease_time))) if lease_time.isdigit() else 'Permanent',
530
- ))
531
-
532
- return dhcp_leases
533
-
534
- def get_ipv4_status(self) -> IPv4Status:
535
- acts = [
536
- self.ActItem(self.ActItem.GS, 'LAN_IP_INTF',
537
- attrs=['X_TP_MACAddress', 'IPInterfaceIPAddress', 'IPInterfaceSubnetMask']),
538
- self.ActItem(self.ActItem.GET, 'LAN_HOST_CFG', '1,0,0,0,0,0', attrs=['DHCPServerEnable']),
539
- self.ActItem(self.ActItem.GS, 'WAN_IP_CONN',
540
- attrs=['enable', 'MACAddress', 'externalIPAddress', 'defaultGateway', 'name', 'subnetMask',
541
- 'DNSServers']),
542
- ]
543
- _, values = self.req_act(acts)
544
-
545
- ipv4_status = IPv4Status()
546
- ipv4_status._lan_macaddr = get_mac(values.get('0', {}).get('X_TP_MACAddress', '00:00:00:00:00:00'))
547
- ipv4_status._lan_ipv4_ipaddr = get_ip(values.get('0', {}).get('IPInterfaceIPAddress', '0.0.0.0'))
548
- ipv4_status._lan_ipv4_netmask = get_ip(values.get('0', {}).get('IPInterfaceSubnetMask', '0.0.0.0'))
549
- ipv4_status.lan_ipv4_dhcp_enable = bool(int(values.get('1', {}).get('DHCPServerEnable', '0')))
550
-
551
- for item in self._to_list(values.get('2')):
552
- if int(item.get('enable', '0')) == 0 and values.get('2').__class__ == list:
553
- continue
554
- ipv4_status._wan_macaddr = get_mac(item.get('MACAddress', '00:00:00:00:00:00'))
555
- ipv4_status._wan_ipv4_ipaddr = get_ip(item.get('externalIPAddress', '0.0.0.0'))
556
- ipv4_status._wan_ipv4_gateway = get_ip(item.get('defaultGateway', '0.0.0.0'))
557
- ipv4_status.wan_ipv4_conntype = item.get('name', '')
558
- ipv4_status._wan_ipv4_netmask = get_ip(item.get('subnetMask', '0.0.0.0'))
559
- dns = item.get('DNSServers', '').split(',')
560
- ipv4_status._wan_ipv4_pridns = get_ip(dns[0] if len(dns) == 2 else '0.0.0.0')
561
- ipv4_status._wan_ipv4_snddns = get_ip(dns[1] if len(dns) == 2 else '0.0.0.0')
562
-
563
- return ipv4_status
564
-
565
- def set_wifi(self, wifi: Connection, enable: bool) -> None:
566
- acts = [
567
- self.ActItem(
568
- self.ActItem.SET,
569
- 'LAN_WLAN' if wifi in [Connection.HOST_2G, Connection.HOST_5G] else 'LAN_WLAN_MSSIDENTRY',
570
- self.WIFI_SET[wifi],
571
- attrs=['enable={}'.format(int(enable))]),
572
- ]
573
- self.req_act(acts)
574
-
575
606
  def send_sms(self, phone_number: str, message: str) -> None:
576
607
  acts = [
577
608
  self.ActItem(
@@ -681,33 +712,3 @@ class TPLinkMRClient(TPLinkMRClientBase):
681
712
  status.isp_name = values['3']['ispName']
682
713
 
683
714
  return status
684
-
685
- def get_vpn_status(self) -> VPNStatus:
686
- status = VPNStatus()
687
- acts = [
688
- self.ActItem(self.ActItem.GET, 'OPENVPN', attrs=['enable']),
689
- self.ActItem(self.ActItem.GET, 'PPTPVPN', attrs=['enable']),
690
- self.ActItem(self.ActItem.GL, 'OVPN_CLIENT', attrs=['connAct']),
691
- self.ActItem(self.ActItem.GL, 'PVPN_CLIENT', attrs=['connAct']),
692
- ]
693
- _, values = self.req_act(acts)
694
-
695
- status.openvpn_enable = values['0']['enable'] == '1'
696
- status.pptpvpn_enable = values['1']['enable'] == '1'
697
-
698
- for item in values['2']:
699
- if item['connAct'] == '1':
700
- status.openvpn_clients_total += 1
701
-
702
- for item in values['3']:
703
- if item['connAct'] == '1':
704
- status.pptpvpn_clients_total += 1
705
-
706
- return status
707
-
708
- def set_vpn(self, vpn: VPN, enable: bool) -> None:
709
- acts = [
710
- self.ActItem(self.ActItem.SET, vpn.value, attrs=['enable={}'.format(int(enable))])
711
- ]
712
-
713
- self.req_act(acts)
@@ -0,0 +1,129 @@
1
+ import base64
2
+ from http import HTTPStatus
3
+ from tplinkrouterc6u.client.mr import TPLinkMRClientBase
4
+ from tplinkrouterc6u.common.exception import ClientException
5
+ from logging import Logger
6
+
7
+
8
+ class TPLinkVRClient(TPLinkMRClientBase):
9
+ def __init__(self, host: str, password: str, username: str = 'admin', logger: Logger = None,
10
+ verify_ssl: bool = True, timeout: int = 30):
11
+ super().__init__(host, password, username, logger, verify_ssl, timeout)
12
+ self._url_rsa_key = 'cgi/getGDPRParm'
13
+
14
+ def supports(self):
15
+ return self._verify_router() and super().supports()
16
+
17
+ def _verify_router(self) -> bool:
18
+ """
19
+ Verifies if the connected router is a supported TP-Link VR model.
20
+
21
+ This function checks if the router is a TP-Link VR model by sending a GET request
22
+ to the host and analyzing the response. It verifies the presence of specific
23
+ keywords and endpoints in the response to determine the model type.
24
+
25
+ Returns:
26
+ bool: True if the router is a supported TP-Link VR model and supports the RSA key endpoint,
27
+ otherwise False.
28
+
29
+ Raises:
30
+ Exception: If an error occurs during the request process, it logs the error
31
+ and returns False.
32
+ """
33
+
34
+ is_VR = False
35
+ has_url_rsa_endpoint = False
36
+
37
+ try:
38
+ status_code, response = self._request(self.host, method='GET')
39
+ except Exception as e:
40
+ if self._logger is not None:
41
+ self._logger.error("Error while checking modem: {}".format(e))
42
+
43
+ return False
44
+
45
+ if status_code == HTTPStatus.OK:
46
+ is_VR = "Archer VR" in response
47
+ has_url_rsa_endpoint = self._url_rsa_key in response
48
+
49
+ if has_url_rsa_endpoint and is_VR:
50
+ return True
51
+ elif is_VR:
52
+ # check if lib.js is present. If response code is 200, it is okay. Check if self._url_rsa_key is present
53
+ try:
54
+ status_code, response = self._request("{}/js/lib.js".format(self.host), method='GET')
55
+ except Exception as e:
56
+ if self._logger is not None:
57
+ self._logger.error("Error while checking if lib.js is present in modem: {}".format(e))
58
+
59
+ # if lib.js is not present, return False. Are API not compatible to this class?
60
+ return False
61
+
62
+ if status_code == HTTPStatus.OK:
63
+ has_url_rsa_endpoint = (self._url_rsa_key in response)
64
+
65
+ return is_VR and has_url_rsa_endpoint
66
+ else:
67
+ # modem is not VR
68
+ return False
69
+
70
+ def logout(self) -> None:
71
+ '''
72
+ Logs out from the host
73
+ '''
74
+ acts = [
75
+ self.ActItem(self.ActItem.CGI, '/cgi/logout')
76
+ ]
77
+
78
+ response, _ = self.req_act(acts)
79
+
80
+ if response == '[cgi]0\n[error]0\n':
81
+ self._token = None
82
+
83
+ def _req_login(self) -> None:
84
+ '''
85
+ Authenticates to the host
86
+ - sets the session token after successful login
87
+ - data/signature is passed as a GET parameter, NOT as a raw request data
88
+ (unlike for regular encrypted requests to the /cgi_gdpr endpoint)
89
+
90
+ Example session token (set as a cookie):
91
+ {'JSESSIONID': '4d786fede0164d7613411c7b6ec61e'}
92
+ '''
93
+ # self.password to base64 string
94
+ base64pwd = base64.b64encode(self.password.encode('utf-8')).decode('utf-8')
95
+ # sign, data = self._prepare_data(self.username + '\n' + str(base64pwd), True)
96
+
97
+ data_list = []
98
+ data_list.append("UserName={}".format(self.username))
99
+ data_list.append("Passwd={}".format(base64pwd))
100
+
101
+ actItem = self.ActItem(self.ActItem.CGI, '/cgi/login', attrs=data_list)
102
+ response, _ = self.req_act([actItem])
103
+
104
+ ret_code = self._parse_ret_val(response)
105
+ if ret_code == self.HTTP_RET_OK:
106
+ return
107
+
108
+ if ret_code == self.HTTP_ERR_USER_PWD_NOT_CORRECT:
109
+
110
+ error = 'TplinkRouter - VR - Login failed, wrong password.'
111
+ if self._logger:
112
+ self._logger.debug(error)
113
+ raise ClientException(error)
114
+
115
+ if ret_code == self.HTTP_ERR_USER_BAD_REQUEST:
116
+ error = 'TplinkRouter - VR - Login failed. Generic error code: {}'.format(ret_code)
117
+ if self._logger:
118
+ self._logger.debug(error)
119
+ raise ClientException(error)
120
+
121
+ # unknown error
122
+ error = 'TplinkRouter - VR - Login failed. Unknown error code: {}'.format(ret_code)
123
+ if self._logger:
124
+ self._logger.debug(error)
125
+ raise ClientException(error)
126
+
127
+ def _request(self, url, method='POST', data_str=None, encrypt=False, is_login=False):
128
+ is_login = encrypt and '/cgi/login' in data_str
129
+ return super()._request(url, method, data_str, encrypt, is_login)
@@ -1,7 +1,7 @@
1
1
  from requests.packages import urllib3
2
2
  from logging import Logger
3
3
  from tplinkrouterc6u.common.package_enum import Connection
4
- from tplinkrouterc6u.common.dataclass import Firmware, Status
4
+ from tplinkrouterc6u.common.dataclass import Firmware, Status, IPv4Status
5
5
  from abc import ABC, abstractmethod
6
6
 
7
7
 
@@ -39,6 +39,10 @@ class AbstractRouter(ABC):
39
39
  def get_status(self) -> Status:
40
40
  pass
41
41
 
42
+ @abstractmethod
43
+ def get_ipv4_status(self) -> IPv4Status:
44
+ pass
45
+
42
46
  @abstractmethod
43
47
  def reboot(self) -> None:
44
48
  pass
@@ -68,6 +68,7 @@ class Status:
68
68
  self.wan_ipv4_uptime: int | None = None
69
69
  self.mem_usage: float | None = None
70
70
  self.cpu_usage: float | None = None
71
+ self.conn_type: str | None = None
71
72
  self.devices: list[Device] = []
72
73
 
73
74
  @property
@@ -167,7 +168,7 @@ class IPv4Status:
167
168
  self._wan_macaddr: EUI48
168
169
  self._wan_ipv4_ipaddr: IPv4Address | None = None
169
170
  self._wan_ipv4_gateway: IPv4Address | None = None
170
- self.wan_ipv4_conntype: str
171
+ self._wan_ipv4_conntype: str
171
172
  self._wan_ipv4_netmask: IPv4Address | None = None
172
173
  self._wan_ipv4_pridns: IPv4Address
173
174
  self._wan_ipv4_snddns: IPv4Address
@@ -189,6 +190,10 @@ class IPv4Status:
189
190
  def wan_ipv4_ipaddr(self):
190
191
  return str(self._wan_ipv4_ipaddr) if self._wan_ipv4_ipaddr else None
191
192
 
193
+ @property
194
+ def wan_ipv4_conntype(self):
195
+ return self._wan_ipv4_conntype if hasattr(self, '_wan_ipv4_conntype') else ''
196
+
192
197
  @property
193
198
  def wan_ipv4_ipaddress(self):
194
199
  return self._wan_ipv4_ipaddr
@@ -16,12 +16,12 @@ def get_mac(mac: str) -> EUI48:
16
16
  return EUI48('00:00:00:00:00:00')
17
17
 
18
18
 
19
- def get_value(dictionary: dict, keys: list):
19
+ def get_value(dictionary, keys: list, default=None):
20
20
  nested_dict = dictionary
21
21
 
22
22
  for key in keys:
23
23
  try:
24
24
  nested_dict = nested_dict[key]
25
25
  except Exception:
26
- return None
26
+ return default
27
27
  return nested_dict
@@ -1,7 +1,7 @@
1
1
  from logging import Logger
2
2
 
3
3
  from tplinkrouterc6u import TPLinkXDRClient
4
- from tplinkrouterc6u.common.exception import ClientException, AuthorizeError
4
+ from tplinkrouterc6u.common.exception import ClientException
5
5
  from tplinkrouterc6u.client.c6u import TplinkRouter
6
6
  from tplinkrouterc6u.client.deco import TPLinkDecoClient
7
7
  from tplinkrouterc6u.client_abstract import AbstractRouter
@@ -10,30 +10,36 @@ from tplinkrouterc6u.client.ex import TPLinkEXClient
10
10
  from tplinkrouterc6u.client.c6v4 import TplinkC6V4Router
11
11
  from tplinkrouterc6u.client.c5400x import TplinkC5400XRouter
12
12
  from tplinkrouterc6u.client.c1200 import TplinkC1200Router
13
+ from tplinkrouterc6u.client.vr import TPLinkVRClient
13
14
 
14
15
 
15
16
  class TplinkRouterProvider:
16
17
  @staticmethod
17
18
  def get_client(host: str, password: str, username: str = 'admin', logger: Logger = None,
18
19
  verify_ssl: bool = True, timeout: int = 30) -> AbstractRouter:
19
- for client in [TplinkC5400XRouter, TPLinkEXClient, TPLinkMRClient, TplinkC6V4Router, TPLinkDecoClient,
20
- TPLinkXDRClient, TplinkRouter]:
20
+ for client in [TplinkC5400XRouter, TPLinkVRClient, TPLinkEXClient, TPLinkMRClient, TplinkC6V4Router,
21
+ TPLinkDecoClient, TPLinkXDRClient, TplinkRouter]:
21
22
  router = client(host, password, username, logger, verify_ssl, timeout)
22
23
  if router.supports():
23
24
  return router
24
25
 
26
+ message = ('Login failed! Please check if your router local password is correct or '
27
+ 'try to use web encrypted password instead. Check the documentation!')
25
28
  router = TplinkC1200Router(host, password, username, logger, verify_ssl, timeout)
26
29
  try:
27
30
  router.authorize()
28
31
  return router
29
- except AuthorizeError as e:
30
- if logger:
31
- logger.error(e.__str__())
32
- raise ClientException(('Login failed! Please check if your router local password is correct or '
33
- 'try to use web encrypted password instead. Check the documentation!'
34
- ))
35
- except Exception as e:
36
- if logger:
37
- logger.error(e.__str__())
38
- raise ClientException('Try to use web encrypted password instead. Check the documentation! '
39
- + e.__str__())
32
+ except Exception:
33
+ pass
34
+
35
+ for client in [TPLinkVRClient, TPLinkXDRClient]:
36
+ router = client(host, password, username, None, verify_ssl, timeout)
37
+ try:
38
+ router.authorize()
39
+ message = ('Your router might be supported by {}. Please open the issue here '
40
+ 'https://github.com/AlexandrErohin/TP-Link-Archer-C6U').format(router.__class__)
41
+ break
42
+ except Exception:
43
+ pass
44
+
45
+ raise ClientException(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tplinkrouterc6u
3
- Version: 5.4.1
3
+ Version: 5.5.0
4
4
  Summary: TP-Link Router API
5
5
  Home-page: https://github.com/AlexandrErohin/TP-Link-Archer-C6U
6
6
  Author: Alex Erohin
@@ -53,6 +53,7 @@ from tplinkrouterc6u import (
53
53
  TplinkC1200Router,
54
54
  TplinkC5400XRouter,
55
55
  TPLinkMRClient,
56
+ TPLinkVRClient,
56
57
  TPLinkEXClient,
57
58
  TPLinkXDRClient,
58
59
  TPLinkDecoClient,
@@ -167,6 +168,7 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
167
168
  | wan_ipv4_uptime | Internet Uptime | int, None |
168
169
  | mem_usage | Memory usage in percentage between 0 and 1 | float, None |
169
170
  | cpu_usage | CPU usage in percentage between 0 and 1 | float, None |
171
+ | conn_type | Connection type | str, None |
170
172
  | devices | List of all connectedd devices | list[[Device](#device)] |
171
173
 
172
174
  ### <a id="device">Device</a>
@@ -318,7 +320,9 @@ or you have TP-link C5400X or similar router you need to get web encrypted passw
318
320
  - Archer MR600 (v1, v2, v3)
319
321
  - Archer VR600 v3
320
322
  - Archer VR900v
323
+ - Archer VR1200v v1
321
324
  - Archer VR2100v v1
325
+ - Archer VX1800v v1.0
322
326
  - Deco M4 2.0
323
327
  - Deco M4R 2.0
324
328
  - Deco M5 v3
@@ -0,0 +1,31 @@
1
+ test/__init__.py,sha256=McQmUjeN3AwmwdS6QNfwGXXE77OKoPK852I2BM9XsrU,210
2
+ test/test_client_c1200.py,sha256=4RdwMEwTGWLOxOVTuPikifXDcUJFQ13DuyWGm2Aevrk,8811
3
+ test/test_client_c6u.py,sha256=3nvhybxYxiCVjjLXB3nBYrAkXS8zoLtJibgvsqsTQ3U,32140
4
+ test/test_client_deco.py,sha256=YPLKRD8GoyDYHfRgdXvCk8iVNw8zdMJW-AHVnNbpdTM,31719
5
+ test/test_client_ex.py,sha256=0dVvOJqxpR2xNca99vadfdpxmzW-4sk9X_4tAK8x9-c,21710
6
+ test/test_client_mr.py,sha256=NxTgXv_wN50K8KsJhykoU-TX0ddDGlX_7jp50FXOLjg,33517
7
+ test/test_client_xdr.py,sha256=mgn-xL5mD5sHD8DjTz9vpY7jeh4Ob6Um6Y8v5Qgx2jA,23374
8
+ tplinkrouterc6u/__init__.py,sha256=8f-K4p6y_bIa4NReODulceWsjCCojAFSSj5vrjaogdw,953
9
+ tplinkrouterc6u/client_abstract.py,sha256=3UYzmll774S_Gb5E0FTVO_rI3-XFM7PSklg1-V-2jls,1419
10
+ tplinkrouterc6u/provider.py,sha256=lgmGAZI5E1jgpo0IRY4TIKTbAb4S_wX3HhRcx0RoSmY,2083
11
+ tplinkrouterc6u/client/__init__.py,sha256=KBy3fmtA9wgyFrb0Urh2x4CkKtWVnESdp-vxmuOvq0k,27
12
+ tplinkrouterc6u/client/c1200.py,sha256=_nY_pJ-wPWODAaes9kHPdVcM6YM54f1E54CfdoFHqbE,4771
13
+ tplinkrouterc6u/client/c5400x.py,sha256=9E0omBSbWY_ljrs5MTCMu5brmrLtzsDB5O62Db8lP8Q,4329
14
+ tplinkrouterc6u/client/c6u.py,sha256=TbS8qbwfPzIVDEMNNlKMhWuGeo-RUvaCkG_bF7Y4L0Q,17637
15
+ tplinkrouterc6u/client/c6v4.py,sha256=bvquZ0FHgU_LJfZxQkfyKQz0C52mFytC4mzFSOqp99Y,1596
16
+ tplinkrouterc6u/client/deco.py,sha256=7uVd9wt3aJGShft7L520AzFpQ85xbgRWM-8uyrpxE8Y,8229
17
+ tplinkrouterc6u/client/ex.py,sha256=Ih0aCvjhUGN6MDIeRgT0nSBQYNWzH9c70i71AN_G_Ns,13039
18
+ tplinkrouterc6u/client/mr.py,sha256=DJ5phVte-yEMddE5QAUu2MnRBmfuBZwz2-4ZWYWXlI0,25956
19
+ tplinkrouterc6u/client/vr.py,sha256=7Tbu0IrWtr4HHtyrnLFXEJi1QctzhilciL7agtwQ0R8,5025
20
+ tplinkrouterc6u/client/xdr.py,sha256=9YuVfrmsrDeqnhJXp1Fc4OfcJEz9SkjdmpOoTIDi6Js,10327
21
+ tplinkrouterc6u/common/__init__.py,sha256=pCTvVZ9CAwgb7MxRnLx0y1rI0sTKSwT24FfxWfQXeTM,33
22
+ tplinkrouterc6u/common/dataclass.py,sha256=QQTGb9w4Yt3GNkISgrQhvWPMEEJOsgFFZ2MI1MIAPjw,7807
23
+ tplinkrouterc6u/common/encryption.py,sha256=4HelTxzN6esMfDZRBt3m8bwB9Nj_biKijnCnrGWPWKg,6228
24
+ tplinkrouterc6u/common/exception.py,sha256=_0G8ZvW5__CsGifHrsZeULdl8c6EUD071sDCQsQgrHY,140
25
+ tplinkrouterc6u/common/helper.py,sha256=23b04fk9HuVinrZXMCS5R1rmF8uZ7eM-Cdnp7Br9NR0,572
26
+ tplinkrouterc6u/common/package_enum.py,sha256=4ykL_2Pw0nDEIH_qR9UJlFF6stTgSfhPz32r8KT-sh8,1624
27
+ tplinkrouterc6u-5.5.0.dist-info/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
28
+ tplinkrouterc6u-5.5.0.dist-info/METADATA,sha256=tBsmDyFWC6vjlTys6G38pTZ658kQMgGhiGgTNplPyRw,15295
29
+ tplinkrouterc6u-5.5.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
30
+ tplinkrouterc6u-5.5.0.dist-info/top_level.txt,sha256=1iSCCIueqgEkrTxtQ-jiHe99jAB10zqrVdBcwvNfe_M,21
31
+ tplinkrouterc6u-5.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.1)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,30 +0,0 @@
1
- test/__init__.py,sha256=McQmUjeN3AwmwdS6QNfwGXXE77OKoPK852I2BM9XsrU,210
2
- test/test_client_c1200.py,sha256=4RdwMEwTGWLOxOVTuPikifXDcUJFQ13DuyWGm2Aevrk,8811
3
- test/test_client_c6u.py,sha256=rJKJWwUBcmMehvdyyRtWm420X6KIXdrJu75VH6JPvng,32066
4
- test/test_client_deco.py,sha256=YPLKRD8GoyDYHfRgdXvCk8iVNw8zdMJW-AHVnNbpdTM,31719
5
- test/test_client_ex.py,sha256=YGY3auCiZTM91640LHmsL6r2-xcK5n5cB_CpvD83GeI,20429
6
- test/test_client_mr.py,sha256=2ZFNwJGHqFbnePRvmmg5r_4YzqsHo3bjkmBXVzht7YQ,33407
7
- test/test_client_xdr.py,sha256=mgn-xL5mD5sHD8DjTz9vpY7jeh4Ob6Um6Y8v5Qgx2jA,23374
8
- tplinkrouterc6u/__init__.py,sha256=bLlCkiL9ycyRVG7VqPOJWPcoPmUfMkdTcUKFYKNP8QI,900
9
- tplinkrouterc6u/client_abstract.py,sha256=OgftTH6bELzhFWom0MOX4EJe3RNS003ezpHyyRnh2tQ,1328
10
- tplinkrouterc6u/provider.py,sha256=SsyGu_2SZ-a46ADl9aw_0wx0lgbsJhS6wnwUzb_1qGg,1900
11
- tplinkrouterc6u/client/__init__.py,sha256=KBy3fmtA9wgyFrb0Urh2x4CkKtWVnESdp-vxmuOvq0k,27
12
- tplinkrouterc6u/client/c1200.py,sha256=_nY_pJ-wPWODAaes9kHPdVcM6YM54f1E54CfdoFHqbE,4771
13
- tplinkrouterc6u/client/c5400x.py,sha256=9E0omBSbWY_ljrs5MTCMu5brmrLtzsDB5O62Db8lP8Q,4329
14
- tplinkrouterc6u/client/c6u.py,sha256=sDlwtccncuVrBRAzbLxgT5hU7VYNM0LCVtyEs-9bk-Q,17587
15
- tplinkrouterc6u/client/c6v4.py,sha256=-IF7SqTZx56lRAVmiyxK9VKkrmD7srQSJWFvn2Z0DII,1489
16
- tplinkrouterc6u/client/deco.py,sha256=yL1fmDo7j67_iD4qi9gl402C0IGauAtgrA0EGAOR1jg,8228
17
- tplinkrouterc6u/client/ex.py,sha256=PxMKq7Yj-2iJBtSKzgxaJ1D0SrblFwhPWJYyrac0c9Q,12706
18
- tplinkrouterc6u/client/mr.py,sha256=rS9aqAxrzIh0Pl8ZqGQQ9uWA3TzEHijkvY-WESRbVHs,25863
19
- tplinkrouterc6u/client/xdr.py,sha256=9YuVfrmsrDeqnhJXp1Fc4OfcJEz9SkjdmpOoTIDi6Js,10327
20
- tplinkrouterc6u/common/__init__.py,sha256=pCTvVZ9CAwgb7MxRnLx0y1rI0sTKSwT24FfxWfQXeTM,33
21
- tplinkrouterc6u/common/dataclass.py,sha256=__u8I8URLk_AdIEN4Jd8Pswh4ujURlJxw8YunuVStwk,7630
22
- tplinkrouterc6u/common/encryption.py,sha256=4HelTxzN6esMfDZRBt3m8bwB9Nj_biKijnCnrGWPWKg,6228
23
- tplinkrouterc6u/common/exception.py,sha256=_0G8ZvW5__CsGifHrsZeULdl8c6EUD071sDCQsQgrHY,140
24
- tplinkrouterc6u/common/helper.py,sha256=x9Y0zkCnELpzNzuQtq2yMrWmp7nCyJylTBKu4a8RfKc,561
25
- tplinkrouterc6u/common/package_enum.py,sha256=4ykL_2Pw0nDEIH_qR9UJlFF6stTgSfhPz32r8KT-sh8,1624
26
- tplinkrouterc6u-5.4.1.dist-info/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
27
- tplinkrouterc6u-5.4.1.dist-info/METADATA,sha256=Z1zydhvJZQU6Ffx_8vpUQgyNjUw4tABTmeTLx1xaabs,15189
28
- tplinkrouterc6u-5.4.1.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
29
- tplinkrouterc6u-5.4.1.dist-info/top_level.txt,sha256=1iSCCIueqgEkrTxtQ-jiHe99jAB10zqrVdBcwvNfe_M,21
30
- tplinkrouterc6u-5.4.1.dist-info/RECORD,,