tplinkrouterc6u 5.0.3__py3-none-any.whl → 5.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ from re import search
2
+ from requests import post
3
+ from tplinkrouterc6u.common.package_enum import Connection
4
+ from tplinkrouterc6u.common.exception import ClientException
5
+ from tplinkrouterc6u.client.c6u import TplinkBaseRouter
6
+
7
+
8
+ class TplinkC5400XRouter(TplinkBaseRouter):
9
+ def supports(self) -> bool:
10
+ return len(self.password) >= 200
11
+
12
+ def authorize(self) -> None:
13
+ if len(self.password) < 200:
14
+ raise Exception('You need to use web encrypted password instead. Check the documentation!')
15
+
16
+ url = '{}/cgi-bin/luci/;stok=/login?form=login'.format(self.host)
17
+
18
+ response = post(
19
+ url,
20
+ params={'operation': 'login', 'username': self.username, 'password': self.password},
21
+ headers=self._headers_login,
22
+ timeout=self.timeout,
23
+ verify=self._verify_ssl,
24
+ )
25
+
26
+ try:
27
+ self._stok = response.json().get('data').get('stok')
28
+ regex_result = search('sysauth=(.*);', response.headers['set-cookie'])
29
+ self._sysauth = regex_result.group(1)
30
+ self._logged = True
31
+ self._smart_network = False
32
+
33
+ except Exception as e:
34
+ error = "TplinkRouter - C5400X - Cannot authorize! Error - {}; Response - {}".format(e, response.text)
35
+ if self._logger:
36
+ self._logger.debug(error)
37
+ raise ClientException(error)
38
+
39
+ def set_led(self, enable: bool) -> None:
40
+ current_state = (self.request('admin/ledgeneral?form=setting&operation=read', 'operation=read')
41
+ .get('enable', 'off') == 'on')
42
+ if current_state != enable:
43
+ self.request('admin/ledgeneral?form=setting&operation=write', 'operation=write')
44
+
45
+ def get_led(self) -> bool:
46
+
47
+ data = self.request('admin/ledgeneral?form=setting&operation=read', 'operation=read')
48
+ led_status = data.get('enable') if 'enable' in data else None
49
+ if led_status == 'on':
50
+ return True
51
+ elif led_status == 'off':
52
+ return False
53
+ else:
54
+ return None
55
+
56
+ def set_wifi(self, wifi: Connection, enable: bool = None, ssid: str = None, hidden: str = None,
57
+ encryption: str = None, psk_version: str = None, psk_cipher: str = None, psk_key: str = None,
58
+ hwmode: str = None, htmode: str = None, channel: int = None, txpower: str = None,
59
+ disabled_all: str = None) -> None:
60
+ values = {
61
+ Connection.HOST_2G: 'wireless_2g',
62
+ Connection.HOST_5G: 'wireless_5g',
63
+ Connection.HOST_6G: 'wireless_6g',
64
+ Connection.GUEST_2G: 'guest_2g',
65
+ Connection.GUEST_5G: 'guest_5g',
66
+ Connection.GUEST_6G: 'guest_6g',
67
+ Connection.IOT_2G: 'iot_2g',
68
+ Connection.IOT_5G: 'iot_5g',
69
+ Connection.IOT_6G: 'iot_6g',
70
+ }
71
+
72
+ value = values.get(wifi)
73
+ if not value:
74
+ raise ValueError(f"Invalid Wi-Fi connection type: {wifi}")
75
+
76
+ if all(v is None for v in [enable, ssid, hidden, encryption, psk_version, psk_cipher, psk_key, hwmode,
77
+ htmode, channel, txpower, disabled_all]):
78
+ raise ValueError("At least one wireless setting must be provided")
79
+
80
+ data = "operation=write"
81
+
82
+ if enable is not None:
83
+ data += f"&enable={'on' if enable else 'off'}"
84
+ if ssid is not None:
85
+ data += f"&ssid={ssid}"
86
+ if hidden is not None:
87
+ data += f"&hidden={hidden}"
88
+ if encryption is not None:
89
+ data += f"&encryption={encryption}"
90
+ if psk_version is not None:
91
+ data += f"&psk_version={psk_version}"
92
+ if psk_cipher is not None:
93
+ data += f"&psk_cipher={psk_cipher}"
94
+ if psk_key is not None:
95
+ data += f"&psk_key={psk_key}"
96
+ if hwmode is not None:
97
+ data += f"&hwmode={hwmode}"
98
+ if htmode is not None:
99
+ data += f"&htmode={htmode}"
100
+ if channel is not None:
101
+ data += f"&channel={channel}"
102
+ if txpower is not None:
103
+ data += f"&txpower={txpower}"
104
+ if disabled_all is not None:
105
+ data += f"&disabled_all={disabled_all}"
106
+
107
+ path = f"admin/wireless?form={value}&{data}"
108
+
109
+ self.request(path, data)
@@ -0,0 +1,436 @@
1
+ from hashlib import md5
2
+ from re import search
3
+ from json import loads
4
+ from requests import post, Response
5
+ from macaddress import EUI48
6
+ from ipaddress import IPv4Address
7
+ from logging import Logger
8
+ from tplinkrouterc6u.common.helper import get_ip, get_mac
9
+ from tplinkrouterc6u.common.encryption import EncryptionWrapper
10
+ from tplinkrouterc6u.common.package_enum import Connection
11
+ from tplinkrouterc6u.common.dataclass import Firmware, Status, Device, IPv4Reservation, IPv4DHCPLease, IPv4Status
12
+ from tplinkrouterc6u.common.exception import ClientException, ClientError
13
+ from tplinkrouterc6u.client_abstract import AbstractRouter
14
+ from abc import abstractmethod
15
+
16
+
17
+ class TplinkRequest:
18
+ host = ''
19
+ _stok = ''
20
+ timeout = 10
21
+ _logged = False
22
+ _sysauth = None
23
+ _verify_ssl = False
24
+ _logger = None
25
+ _headers_request = {}
26
+ _headers_login = {}
27
+ _data_block = 'data'
28
+
29
+ def request(self, path: str, data: str, ignore_response: bool = False, ignore_errors: bool = False) -> dict | None:
30
+ if self._logged is False:
31
+ raise Exception('Not authorised')
32
+ url = '{}/cgi-bin/luci/;stok={}/{}'.format(self.host, self._stok, path)
33
+
34
+ response = post(
35
+ url,
36
+ data=self._prepare_data(data),
37
+ headers=self._headers_request,
38
+ cookies={'sysauth': self._sysauth},
39
+ timeout=self.timeout,
40
+ verify=self._verify_ssl,
41
+ )
42
+
43
+ if ignore_response:
44
+ return None
45
+
46
+ data = response.text
47
+ error = ''
48
+ try:
49
+ data = response.json()
50
+ if 'data' not in data:
51
+ raise Exception("Router didn't respond with JSON")
52
+ data = self._decrypt_response(data)
53
+
54
+ if self._is_valid_response(data):
55
+ return data.get(self._data_block)
56
+ elif ignore_errors:
57
+ return data
58
+ except Exception as e:
59
+ error = ('TplinkRouter - {} - An unknown response - {}; Request {} - Response {}'
60
+ .format(self.__class__.__name__, e, path, data))
61
+ error = ('TplinkRouter - {} - Response with error; Request {} - Response {}'
62
+ .format(self.__class__.__name__, path, data)) if not error else error
63
+ if self._logger:
64
+ self._logger.debug(error)
65
+ raise ClientError(error)
66
+
67
+ def _is_valid_response(self, data: dict) -> bool:
68
+ return 'success' in data and data['success'] and self._data_block in data
69
+
70
+ def _prepare_data(self, data: str):
71
+ return data
72
+
73
+ def _decrypt_response(self, data: dict) -> dict:
74
+ return data
75
+
76
+
77
+ class TplinkEncryption(TplinkRequest):
78
+ username = ''
79
+ password = ''
80
+ nn = ''
81
+ ee = ''
82
+ _seq = ''
83
+ _pwdNN = ''
84
+ _pwdEE = ''
85
+ _encryption = EncryptionWrapper()
86
+
87
+ def supports(self) -> bool:
88
+ if len(self.password) > 125:
89
+ return False
90
+
91
+ try:
92
+ self._request_pwd()
93
+ return True
94
+ except ClientException:
95
+ return False
96
+
97
+ def authorize(self) -> None:
98
+ if self._pwdNN == '':
99
+ self._request_pwd()
100
+
101
+ if self._seq == '':
102
+ self._request_seq()
103
+
104
+ response = self._try_login()
105
+
106
+ is_valid_json = False
107
+ try:
108
+ response.json()
109
+ is_valid_json = True
110
+ except BaseException:
111
+ """Ignore"""
112
+
113
+ if is_valid_json is False or response.status_code == 403:
114
+ self._logged = False
115
+ self._request_pwd()
116
+ self._request_seq()
117
+ response = self._try_login()
118
+
119
+ data = response.text
120
+ try:
121
+ data = response.json()
122
+ data = self._decrypt_response(data)
123
+
124
+ self._stok = data[self._data_block]['stok']
125
+ regex_result = search(
126
+ 'sysauth=(.*);', response.headers['set-cookie'])
127
+ self._sysauth = regex_result.group(1)
128
+ self._logged = True
129
+
130
+ except Exception as e:
131
+ error = ("TplinkRouter - {} - Cannot authorize! Error - {}; Response - {}"
132
+ .format(self.__class__.__name__, e, data))
133
+ if self._logger:
134
+ self._logger.debug(error)
135
+ raise ClientException(error)
136
+
137
+ def _request_pwd(self) -> None:
138
+ url = '{}/cgi-bin/luci/;stok=/login?form=keys'.format(self.host)
139
+
140
+ # If possible implement RSA encryption of password here.
141
+ response = post(
142
+ url, params={'operation': 'read'},
143
+ timeout=self.timeout,
144
+ verify=self._verify_ssl,
145
+ )
146
+
147
+ try:
148
+ data = response.json()
149
+
150
+ args = data[self._data_block]['password']
151
+
152
+ self._pwdNN = args[0]
153
+ self._pwdEE = args[1]
154
+
155
+ except Exception as e:
156
+ error = ('TplinkRouter - {} - Unknown error for pwd! Error - {}; Response - {}'
157
+ .format(self.__class__.__name__, e, response.text))
158
+ if self._logger:
159
+ self._logger.debug(error)
160
+ raise ClientException(error)
161
+
162
+ def _request_seq(self) -> None:
163
+ url = '{}/cgi-bin/luci/;stok=/login?form=auth'.format(self.host)
164
+
165
+ # If possible implement RSA encryption of password here.
166
+ response = post(
167
+ url,
168
+ params={'operation': 'read'},
169
+ timeout=self.timeout,
170
+ verify=self._verify_ssl,
171
+ )
172
+
173
+ try:
174
+ data = response.json()
175
+
176
+ self._seq = data[self._data_block]['seq']
177
+ args = data[self._data_block]['key']
178
+
179
+ self.nn = args[0]
180
+ self.ee = args[1]
181
+
182
+ except Exception as e:
183
+ error = ('TplinkRouter - {} - Unknown error for seq! Error - {}; Response - {}'
184
+ .format(self.__class__.__name__, e, response.text))
185
+ if self._logger:
186
+ self._logger.debug(error)
187
+ raise ClientException(error)
188
+
189
+ def _try_login(self) -> Response:
190
+ url = '{}/cgi-bin/luci/;stok=/login?form=login'.format(self.host)
191
+
192
+ crypted_pwd = self._encryption.rsa_encrypt(self.password, self._pwdNN, self._pwdEE)
193
+
194
+ body = self._prepare_data(self._get_login_data(crypted_pwd))
195
+
196
+ return post(
197
+ url,
198
+ data=body,
199
+ headers=self._headers_login,
200
+ timeout=self.timeout,
201
+ verify=self._verify_ssl,
202
+ )
203
+
204
+ @staticmethod
205
+ def _get_login_data(crypted_pwd: str) -> str:
206
+ return 'operation=login&password={}&confirm=true'.format(crypted_pwd)
207
+
208
+ def _prepare_data(self, data: str) -> dict:
209
+ encrypted_data = self._encryption.aes_encrypt(data)
210
+ data_len = len(encrypted_data)
211
+ hash = md5((self.username + self.password).encode()).hexdigest()
212
+
213
+ sign = self._encryption.get_signature(int(self._seq) + data_len,
214
+ True if self._logged is False else False,
215
+ hash, self.nn, self.ee)
216
+
217
+ return {'sign': sign, 'data': encrypted_data}
218
+
219
+ def _decrypt_response(self, data: dict) -> dict:
220
+ return loads(self._encryption.aes_decrypt(data['data']))
221
+
222
+
223
+ class TplinkBaseRouter(AbstractRouter, TplinkRequest):
224
+ _smart_network = True
225
+ _perf_status = True
226
+
227
+ def __init__(self, host: str, password: str, username: str = 'admin', logger: Logger = None,
228
+ verify_ssl: bool = True, timeout: int = 30) -> None:
229
+ super().__init__(host, password, username, logger, verify_ssl, timeout)
230
+
231
+ self._url_firmware = 'admin/firmware?form=upgrade&operation=read'
232
+ self._url_ipv4_reservations = 'admin/dhcps?form=reservation&operation=load'
233
+ self._url_ipv4_dhcp_leases = 'admin/dhcps?form=client&operation=load'
234
+ referer = '{}/webpages/index.html'.format(self.host)
235
+ self._headers_request = {'Referer': referer}
236
+ self._headers_login = {'Referer': referer, 'Content-Type': 'application/x-www-form-urlencoded'}
237
+
238
+ @abstractmethod
239
+ def authorize(self) -> bool:
240
+ pass
241
+
242
+ def set_wifi(self, wifi: Connection, enable: bool) -> None:
243
+ values = {
244
+ Connection.HOST_2G: 'wireless_2g',
245
+ Connection.HOST_5G: 'wireless_5g',
246
+ Connection.HOST_6G: 'wireless_6g',
247
+ Connection.GUEST_2G: 'guest_2g',
248
+ Connection.GUEST_5G: 'guest_5g',
249
+ Connection.GUEST_6G: 'guest_6g',
250
+ Connection.IOT_2G: 'iot_2g',
251
+ Connection.IOT_5G: 'iot_5g',
252
+ Connection.IOT_6G: 'iot_6g',
253
+ }
254
+ value = values.get(wifi)
255
+ path = f"admin/wireless?&form=guest&form={value}"
256
+ data = f"operation=write&{value}_enable={'on' if enable else 'off'}"
257
+ self.request(path, data)
258
+
259
+ def reboot(self) -> None:
260
+ self.request('admin/system?form=reboot', 'operation=write', True)
261
+
262
+ def logout(self) -> None:
263
+ self.request('admin/system?form=logout', 'operation=write', True)
264
+ self._stok = ''
265
+ self._sysauth = ''
266
+ self._logged = False
267
+
268
+ def get_firmware(self) -> Firmware:
269
+ data = self.request(self._url_firmware, 'operation=read')
270
+ firmware = Firmware(data.get('hardware_version', ''), data.get('model', ''), data.get('firmware_version', ''))
271
+
272
+ return firmware
273
+
274
+ def get_status(self) -> Status:
275
+ data = self.request('admin/status?form=all&operation=read', 'operation=read')
276
+
277
+ status = Status()
278
+ status._wan_macaddr = EUI48(data['wan_macaddr']) if 'wan_macaddr' in data else None
279
+ status._lan_macaddr = EUI48(data['lan_macaddr'])
280
+ status._wan_ipv4_addr = IPv4Address(data['wan_ipv4_ipaddr']) if 'wan_ipv4_ipaddr' in data else None
281
+ status._lan_ipv4_addr = IPv4Address(data['lan_ipv4_ipaddr']) if 'lan_ipv4_ipaddr' in data else None
282
+ status._wan_ipv4_gateway = IPv4Address(
283
+ data['wan_ipv4_gateway']) if 'wan_ipv4_gateway' in data else None
284
+ status.wan_ipv4_uptime = data.get('wan_ipv4_uptime')
285
+ status.mem_usage = data.get('mem_usage')
286
+ status.cpu_usage = data.get('cpu_usage')
287
+ status.wired_total = len(data.get('access_devices_wired', []))
288
+ status.wifi_clients_total = len(data.get('access_devices_wireless_host', []))
289
+ status.guest_clients_total = len(data.get('access_devices_wireless_guest', []))
290
+ status.guest_2g_enable = self._str2bool(data.get('guest_2g_enable'))
291
+ status.guest_5g_enable = self._str2bool(data.get('guest_5g_enable'))
292
+ status.guest_6g_enable = self._str2bool(data.get('guest_6g_enable'))
293
+ status.iot_2g_enable = self._str2bool(data.get('iot_2g_enable'))
294
+ status.iot_5g_enable = self._str2bool(data.get('iot_5g_enable'))
295
+ status.iot_6g_enable = self._str2bool(data.get('iot_6g_enable'))
296
+ status.wifi_2g_enable = self._str2bool(data.get('wireless_2g_enable'))
297
+ status.wifi_5g_enable = self._str2bool(data.get('wireless_5g_enable'))
298
+ status.wifi_6g_enable = self._str2bool(data.get('wireless_6g_enable'))
299
+
300
+ if (status.mem_usage is None or status.mem_usage is None) and self._perf_status:
301
+ try:
302
+ performance = self.request('admin/status?form=perf&operation=read', 'operation=read')
303
+ status.mem_usage = performance.get('mem_usage')
304
+ status.cpu_usage = performance.get('cpu_usage')
305
+ except BaseException:
306
+ self._perf_status = False
307
+
308
+ devices = {}
309
+
310
+ def _add_device(conn: Connection, item: dict) -> None:
311
+ devices[item['macaddr']] = Device(conn, get_mac(item.get('macaddr', '00:00:00:00:00:00')),
312
+ get_ip(item['ipaddr']),
313
+ item['hostname'])
314
+
315
+ for item in data.get('access_devices_wired', []):
316
+ type = self._map_wire_type(item.get('wire_type'))
317
+ _add_device(type, item)
318
+
319
+ for item in data.get('access_devices_wireless_host', []):
320
+ type = self._map_wire_type(item.get('wire_type'))
321
+ _add_device(type, item)
322
+
323
+ for item in data.get('access_devices_wireless_guest', []):
324
+ type = self._map_wire_type(item.get('wire_type'), False)
325
+ _add_device(type, item)
326
+
327
+ smart_network = None
328
+ if self._smart_network:
329
+ try:
330
+ smart_network = self.request('admin/smart_network?form=game_accelerator', 'operation=loadDevice')
331
+ except Exception:
332
+ self._smart_network = False
333
+
334
+ if smart_network:
335
+ for item in smart_network:
336
+ if item['mac'] not in devices:
337
+ conn = self._map_wire_type(item.get('deviceTag'), not item.get('isGuest'))
338
+ devices[item['mac']] = Device(conn, get_mac(item.get('mac', '00:00:00:00:00:00')),
339
+ get_ip(item['ip']), item['deviceName'])
340
+ if conn.is_iot():
341
+ if status.iot_clients_total is None:
342
+ status.iot_clients_total = 0
343
+ status.iot_clients_total += 1
344
+
345
+ devices[item['mac']].down_speed = item.get('downloadSpeed')
346
+ devices[item['mac']].up_speed = item.get('uploadSpeed')
347
+ devices[item['mac']].signal = int(item.get('signal')) if item.get('signal') else None
348
+
349
+ for item in self.request('admin/wireless?form=statistics', 'operation=load'):
350
+ if item['mac'] not in devices:
351
+ status.wifi_clients_total += 1
352
+ type = self._map_wire_type(item.get('type'))
353
+ devices[item['mac']] = Device(type, EUI48(item['mac']), IPv4Address('0.0.0.0'),
354
+ '')
355
+ devices[item['mac']].packets_sent = item.get('txpkts')
356
+ devices[item['mac']].packets_received = item.get('rxpkts')
357
+
358
+ status.devices = list(devices.values())
359
+ status.clients_total = status.wired_total + status.wifi_clients_total + status.guest_clients_total
360
+
361
+ return status
362
+
363
+ def get_ipv4_status(self) -> IPv4Status:
364
+ ipv4_status = IPv4Status()
365
+ data = self.request('admin/network?form=status_ipv4&operation=read', 'operation=read')
366
+ ipv4_status._wan_macaddr = EUI48(data['wan_macaddr'])
367
+ ipv4_status._wan_ipv4_ipaddr = IPv4Address(data['wan_ipv4_ipaddr'])
368
+ ipv4_status._wan_ipv4_gateway = IPv4Address(data['wan_ipv4_gateway'])
369
+ ipv4_status.wan_ipv4_conntype = data['wan_ipv4_conntype']
370
+ ipv4_status._wan_ipv4_netmask = IPv4Address(data['wan_ipv4_netmask'])
371
+ ipv4_status._wan_ipv4_pridns = IPv4Address(data['wan_ipv4_pridns'])
372
+ ipv4_status._wan_ipv4_snddns = IPv4Address(data['wan_ipv4_snddns'])
373
+ ipv4_status._lan_macaddr = EUI48(data['lan_macaddr'])
374
+ ipv4_status._lan_ipv4_ipaddr = IPv4Address(data['lan_ipv4_ipaddr'])
375
+ ipv4_status.lan_ipv4_dhcp_enable = self._str2bool(data['lan_ipv4_dhcp_enable'])
376
+ ipv4_status._lan_ipv4_netmask = IPv4Address(data['lan_ipv4_netmask'])
377
+ ipv4_status.remote = self._str2bool(data.get('remote'))
378
+
379
+ return ipv4_status
380
+
381
+ def get_ipv4_reservations(self) -> [IPv4Reservation]:
382
+ ipv4_reservations = []
383
+ data = self.request(self._url_ipv4_reservations, 'operation=load')
384
+
385
+ for item in data:
386
+ ipv4_reservations.append(
387
+ IPv4Reservation(EUI48(item['mac']), IPv4Address(item['ip']), item['comment'],
388
+ self._str2bool(item['enable'])))
389
+
390
+ return ipv4_reservations
391
+
392
+ def get_ipv4_dhcp_leases(self) -> [IPv4DHCPLease]:
393
+ dhcp_leases = []
394
+ data = self.request(self._url_ipv4_dhcp_leases, 'operation=load')
395
+
396
+ for item in data:
397
+ dhcp_leases.append(
398
+ IPv4DHCPLease(EUI48(item['macaddr']), IPv4Address(item['ipaddr']), item['name'],
399
+ item['leasetime']))
400
+
401
+ return dhcp_leases
402
+
403
+ @staticmethod
404
+ def _str2bool(v) -> bool | None:
405
+ return str(v).lower() in ("yes", "true", "on") if v is not None else None
406
+
407
+ @staticmethod
408
+ def _map_wire_type(data: str | None, host: bool = True) -> Connection:
409
+ result = Connection.UNKNOWN
410
+ if data is None:
411
+ return result
412
+ if data == 'wired':
413
+ result = Connection.WIRED
414
+ if data.startswith('2.4'):
415
+ result = Connection.HOST_2G if host else Connection.GUEST_2G
416
+ elif data.startswith('5'):
417
+ result = Connection.HOST_5G if host else Connection.GUEST_5G
418
+ elif data.startswith('6'):
419
+ result = Connection.HOST_6G if host else Connection.GUEST_6G
420
+ elif data.startswith('iot_2'):
421
+ result = Connection.IOT_2G
422
+ elif data.startswith('iot_5'):
423
+ result = Connection.IOT_5G
424
+ elif data.startswith('iot_6'):
425
+ result = Connection.IOT_6G
426
+ return result
427
+
428
+
429
+ class TplinkRouter(TplinkEncryption, TplinkBaseRouter):
430
+ def __init__(self, host: str, password: str, username: str = 'admin', logger: Logger = None,
431
+ verify_ssl: bool = True, timeout: int = 30) -> None:
432
+ super().__init__(host, password, username, logger, verify_ssl, timeout)
433
+
434
+ self._url_firmware = 'admin/firmware?form=upgrade'
435
+ self._url_ipv4_reservations = 'admin/dhcps?form=reservation'
436
+ self._url_ipv4_dhcp_leases = 'admin/dhcps?form=client'
@@ -0,0 +1,38 @@
1
+ from requests import post
2
+ from tplinkrouterc6u.common.package_enum import Connection
3
+ from tplinkrouterc6u.common.dataclass import Firmware, Status
4
+ from tplinkrouterc6u.common.exception import ClientException
5
+ from tplinkrouterc6u.client_abstract import AbstractRouter
6
+
7
+
8
+ class TplinkC6V4Router(AbstractRouter):
9
+ def supports(self) -> bool:
10
+ url = '{}/?code=2&asyn=1'.format(self.host)
11
+ try:
12
+ response = post(url, timeout=self.timeout, verify=self._verify_ssl)
13
+ except BaseException:
14
+ return False
15
+ if response.status_code == 401 and response.text.startswith('00'):
16
+ raise ClientException(('Your router is not supported. Please add your router support to '
17
+ 'https://github.com/AlexandrErohin/TP-Link-Archer-C6U '
18
+ 'by implementing methods for TplinkC6V4Router class'
19
+ ))
20
+ return False
21
+
22
+ def authorize(self) -> None:
23
+ raise ClientException('Not Implemented')
24
+
25
+ def logout(self) -> None:
26
+ raise ClientException('Not Implemented')
27
+
28
+ def get_firmware(self) -> Firmware:
29
+ raise ClientException('Not Implemented')
30
+
31
+ def get_status(self) -> Status:
32
+ raise ClientException('Not Implemented')
33
+
34
+ def reboot(self) -> None:
35
+ raise ClientException('Not Implemented')
36
+
37
+ def set_wifi(self, wifi: Connection, enable: bool) -> None:
38
+ raise ClientException('Not Implemented')