Git Product home page Git Product logo

tp-link-archer-c6u's Introduction

TP-Link Router API

Python package for API access and management for TP-Link Routers. See Supported routers

Pypi Downloads Python versions

Installation

pip install tplinkrouterc6u

Dependencies

Usage

Enter the host & credentials used to log in to your router management page. Username is admin by default. But you may pass username as third parameter

from tplinkrouterc6u import (
    TplinkRouterProvider,
    TplinkRouter,
    TplinkC1200Router,
    TPLinkMRClient,
    TPLinkDecoClient,
    Connection
)
from logging import Logger

router = TplinkRouterProvider.get_client('http://192.168.0.1', 'password')
# You may use client directly like
# router = TplinkRouter('http://192.168.0.1', 'password')
# You may also pass username if it is different and a logger to log errors as
# router = TplinkRouter('http://192.168.0.1','password','admin2', Logger('test'))
# If you have the TP-link C1200 V2 or similar, you can use the TplinkC1200Router class instead of the TplinkRouter class.
# Remember that the password for this router is different, here you need to use the web encrypted password.
# To get web encrypted password, read Web Encrypted Password section
# router = TplinkC1200Router('http://192.168.0.1','WebEncryptedPassword', Logger('test'))

try:
    router.authorize()  # authorizing
    # Get firmware info - returns Firmware
    firmware = router.get_firmware()

    # Get status info - returns Status
    status = router.get_status()
    if not status.guest_2g_enable:  # check if guest 2.4G wifi is disable
        router.set_wifi(Connection.GUEST_2G, True)  # turn on guest 2.4G wifi

    # Get Address reservations, sort by ipaddr
    reservations = router.get_ipv4_reservations()
    reservations.sort(key=lambda a: a.ipaddr)
    for res in reservations:
        print(f"{res.macaddr} {res.ipaddr:16s} {res.hostname:36} {'Permanent':12}")

    # Get DHCP leases, sort by ipaddr
    leases = router.get_ipv4_dhcp_leases()
    leases.sort(key=lambda a: a.ipaddr)
    for lease in leases:
        print(f"{lease.macaddr} {lease.ipaddr:16s} {lease.hostname:36} {lease.lease_time:12}")
finally:
    router.logout()  # always logout as TP-Link Web Interface only supports upto 1 user logged

The TP-Link Web Interface only supports upto 1 user logged in at a time (for security reasons, apparently). So before action you need to authorize and after logout

If you got exception - You need to use web encrypted password instead. Check the documentation! or you have TP-link C1200 V2 or similar router you need to get web encrypted password by these actions:

  1. Go to the login page of your router. (default: 192.168.0.1).
  2. Type in the password you use to login into the password field.
  3. Click somewhere else on the page so that the password field is not selected anymore.
  4. Open the JavaScript console of your browser (usually by pressing F12 and then clicking on "Console").
  5. Type document.getElementById("login-password").value;
  6. Copy the returned value as password and use it.

Functions

Function Args Description Return
get_firmware Gets firmware info about the router Firmware
get_status Gets status about the router info including wifi statuses and connected devices info Status
get_ipv4_status Gets WAN and LAN IPv4 status info, gateway, DNS, netmask IPv4Status
get_ipv4_reservations Gets IPv4 reserved addresses (static) [IPv4Reservation]
get_ipv4_dhcp_leases Gets IPv4 addresses assigned via DHCP [IPv4DHCPLease]
set_wifi wifi: Connection, enable: bool Allow to turn on/of 4 wifi networks
send_sms phone_number: str, message: str Send sms for LTE routers
reboot reboot router
authorize authorize for actions
logout logout after all is done

Dataclass

Field Description Type
hardware_version Returns like - Archer C6U str
model Returns like - Archer C6U v1.0 str
firmware_version Returns like - 1.1.3 Build 3425243 str
Field Description Type
wan_macaddr router wan mac address str, None
wan_macaddress router wan mac address macaddress.EUI48, None
lan_macaddr router lan mac address str
lan_macaddress router lan mac address macaddress.EUI48
wan_ipv4_addr router wan ipv4 address str, None
wan_ipv4_address router wan ipv4 address ipaddress.IPv4Address, None
lan_ipv4_addr router lan ipv4 address str, None
lan_ipv4_address router lan ipv4 address ipaddress.IPv4Address, None
wan_ipv4_gateway router wan ipv4 gateway str, None
wan_ipv4_gateway_address router wan ipv4 gateway address ipaddress.IPv4Address, None
wired_total Total amount of wired clients int
wifi_clients_total Total amount of host wifi clients int
guest_clients_total Total amount of guest wifi clients int
clients_total Total amount of all connected clients int
iot_clients_total Total amount of all iot connected clients int, None
guest_2g_enable Is guest wifi 2.4G enabled bool
guest_5g_enable Is guest wifi 5G enabled bool, None
guest_6g_enable Is guest wifi 6G enabled bool, None
iot_2g_enable Is IoT wifi 2.4G enabled bool, None
iot_5g_enable Is IoT wifi 5G enabled bool, None
iot_6g_enable Is IoT wifi 6G enabled bool, None
wifi_2g_enable Is host wifi 2.4G enabled bool
wifi_5g_enable Is host wifi 5G enabled bool, None
wifi_6g_enable Is host wifi 6G enabled bool, None
wan_ipv4_uptime Internet Uptime int, None
mem_usage Memory usage in percentage between 0 and 1 float, None
cpu_usage CPU usage in percentage between 0 and 1 float, None
devices List of all connectedd devices list[Device]
Field Description Type
type client connection type (2.4G or 5G, guest wifi or host wifi, wired) Connection
macaddr client mac address str
macaddress client mac address macaddress
ipaddr client ip address str
ipaddress client ip address ipaddress
hostname client hostname str
packets_sent total packets sent int, None
packets_received total packets received int, None
down_speed download speed int, None
up_speed upload speed int, None
Field Description Type
macaddr client mac address str
macaddress client mac address macaddress
ipaddr client ip address str
ipaddress client ip address ipaddress
hostname client hostname str
enabled enabled bool
Field Description Type
macaddr client mac address str
macaddress client mac address macaddress
ipaddr client ip address str
ipaddress client ip address ipaddress
hostname client hostname str
lease_time ip address lease time str
Field Description Type
wan_macaddr router mac address str
wan_macaddress router mac address macaddress
wan_ipv4_ipaddr router mac address str, None
wan_ipv4_ipaddress router mac address ipaddress.IPv4Address, None
wan_ipv4_gateway router WAN gateway IP address str, None
wan_ipv4_gateway_address router WAN gateway IP address ipaddress.IPv4Address, None
wan_ipv4_conntype router connection type str
wan_ipv4_netmask router WAN gateway IP netmask str, None
wan_ipv4_netmask_address router WAN gateway IP netmask ipaddress.IPv4Address, None
wan_ipv4_pridns router primary dns server str
wan_ipv4_pridns_address router primary dns server ipaddress
wan_ipv4_snddns router secondary dns server str
wan_ipv4_snddns_address router secondary dns server ipaddress
lan_macaddr router mac address str
lan_macaddress router mac address macaddress
lan_ipv4_ipaddr router LAN IP address str
lan_ipv4_ipaddress router LAN IP address ipaddress
lan_ipv4_dhcp_enable router LAN DHCP enabled bool
lan_ipv4_netmask router LAN gateway IP netmask str
lan_ipv4_netmask_address router LAN gateway IP netmask ipaddress
remote router remote bool, None

Enum

  • Connection.HOST_2G - host wifi 2.4G
  • Connection.HOST_5G - host wifi 5G
  • Connection.HOST_6G - host wifi 5G
  • Connection.GUEST_2G - guest wifi 2.4G
  • Connection.GUEST_5G - guest wifi 5G
  • Connection.GUEST_6G - guest wifi 5G
  • Connection.IOT_2G - IoT wifi 2.4G
  • Connection.IOT_5G - IoT wifi 5G
  • Connection.IOT_6G - IoT wifi 6G
  • Connection.WIRED - Wired

Fully tested Hardware Versions

  • Archer A7 V5
  • Archer AX10 v1.0
  • Archer AX12 v1.0
  • Archer AX20 v1.0
  • Archer AX20 v3.0
  • Archer AX21 v1.20
  • Archer AX23 v1.0
  • Archer AX50 v1.0
  • Archer AX53 v2
  • Archer AX55 v1.0
  • Archer AX55 V1.60
  • Archer AX72 V1
  • Archer AX73 V1
  • Archer AX75 V1
  • Archer AXE75 V1
  • Archer AXE16000
  • Archer AX3000 V1
  • Archer AX6000 V1
  • Archer AX11000 V1
  • Archer BE805 v1.0
  • Archer C1200 v2.0 (You need to use web encrypted password)
  • Archer C2300 v1.0 (You need to use web encrypted password)
  • Archer C6 v2.0
  • Archer C6 v3.0
  • Archer C6U v1.0
  • Archer C7 v5.0
  • Archer MR200 v5
  • Archer MR200 v5.3
  • Archer MR600 v1
  • Archer MR600 v3
  • Archer VR900v
  • Deco M4 2.0
  • Deco M4R 2.0
  • Deco M5
  • Deco M9 Pro
  • Deco P7
  • Deco X20
  • Deco X60
  • Deco X90
  • Deco XE75 2.0
  • TL-WA3001 v1.0
  • TL-MR105
  • TL-MR6400 v5
  • TL-MR6400 v5.3

Not fully tested Hardware Versions

  • AD7200 V2
  • Archer A6 (V2 and V3)
  • Archer A9 V6
  • Archer A10 (V1 and V2)
  • Archer A20 (V1, V3)
  • Archer C7 V4
  • Archer C8 (V3 and V4)
  • Archer C9 (V4 and V5)
  • Archer C59 V2
  • Archer C90 V6
  • Archer C900 V1
  • Archer C1200 V3
  • Archer C1900 V2
  • Archer C2300 (V1, V2)
  • Archer C4000 (V2 and V3)
  • Archer C5400 V2
  • Archer C5400X V1
  • TD-W9960 v1
  • TL-WR1043N V5

Please let me know if you have tested integration with one of this or other model. Open an issue with info about router's model, hardware and firmware versions.

Guidelines CONTRIBUTING.md

Local Development

  • Download this repository.
  • Run pip install -e path/to/repo.
  • Make changes to files within the tplinkrouter6u directory.
  • Exercise the changes following the "Usage" section above.

The sanity check test.py illustrates a few tests and runs through a list of queries in queries.txt creating logs of the results of each query in the logs folder. This can be used to capture the dictionary output of all cgi-bin form submissions.

Run tests

  • Run python -m unittest discover ./test

Thanks To

tp-link-archer-c6u's People

Contributors

adamgross42 avatar alexandrerohin avatar durgnomis-drol avatar gordonaspin avatar nicholas-l avatar swwgames avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

tp-link-archer-c6u's Issues

Control pppoe dialup

Hello,

I would like to know if it's possible to control the dialing of the router.

In the browser, I captured that both the connection and disconnection XHRs are admin/network?form=wan_ipv4_pppoe

The only difference is in the requested form data.

For connection:

sign: 9ab27491c2a70286de5427f9a3214ef022058edff0c9ffa2ff09458aa2e8f5ef410066f6b3b5b0004cb38cfdc807825acdc3f1cd387b36ec5aa5f347c47bab45
data: fcDYoc6e2v11lqlYL9nB9c5v0S/jrfQYmL0fK9jxtXmo3tYiQmsbjwzF5pYe3rPSJuM65Iyp64WVsXlcvvqQSzxP0doK8qzJWqabFY/uHmj2dMTXlovh5J5vn9Wj7FdKkkF4RxLFITIrMP1pLiCxcb6AMjFOt2Kdb7DVUDD+DhBzIH8ctgZ2UOeKcYQ310P8bIu4nKxE3TWpsHrWkOIB0icp9DV7NalhFfW9QcZ55vPDpx2TpZodzVYB/nAr3Frk7XrZaz6M3FQKtcwvL1mpM0e+nsrLbOTAlQVpZ7UqDfiFGTqw+s4GCDsOp36zei0gkphWJqkSEjm6V/pl8eBK7zvgjQEEm+F/GUE2yhGtOlYIhLYjQUPIWq48bOGmempuLt+vQHrxHLNXREz+hqgU2K42FgKoWZ08thHUiG0wXCyHap7Ks83fc2KlDkzhrpOXFRNGH6hu1caPBcrQTdiloxRptlFxwNDkcr65tIr4Dh4qunmp63kCXE1YcQ5/Jnil

For disconnection:

sign: 265f209f7f08851b689bdf529ecbcee88c58e4cd4333bb24f0e2299a40a0ab1e59d120a49833967a8f05140b0d6824b4305cbd7f32c496dac2ae96a7a839d851
data: THNBLXF2Es2XS/GePB4UcNRmTCV1xKnkZSuHo4BDAlMsAQJJ87mgLRLFkPt0H1a4vAWDHg/411jEBSyJUBH/C9X2z7tNZ3cHdU8bWYF+1ma6Q2BcTsaTgB2qz5jcW0Q59VC8sOf3oiisKM/ukCAZ5fcGegpoQ1sgZDKWA8OfslHkManCrNX9tqMgIyvxvQZ4j6johX3GCnu8bL8JKbICqaj+l/KHfXx8eWEyNs4wpfTt68xVwgG1hQXYEPrgRI29DRRsx2sNR+hRj6QPolt6Qev5QGRnwqrcJwxeYxJzFOeih5ZWTn1Z3U+3BsHsdc5gT9kK3gcxS1OnkJ1TVptroZ6YPxVLRUHVpHSMujfcVlO08ey73hTZA5lTVBKS9/WecBtWC/QjGN3XNpyQRSO3gm16ZeB3JYKuPvDwZ65sw0lsRCUmQSKTTFDi2Sr1PgRgcnULYfB4UAiWFgSOtVV2xRHZD6+I+r3lhjvFmyshzPsy4FVmqVhAt/XBhbVBXRxDNwaxoW8Wd4t0+ApJiaU2A23M58EKXlglkDodFDhRkME=

Tested TP-link C1200 v2

Issue Description:
I tested the TP-Link C1200 v2, and it does not work with the existing code. After troubleshooting, I discovered that this router requires modifications to the client.py code. Specifically, the self._seq and self._pwdNN parts of the code are not needed. To retrieve data, only the stok and sysauth tokens are required.

Changes Made:
I've made the necessary modifications to the client.py code.

import hashlib
import re
from collections.abc import Callable
import json
import requests
import macaddress
import ipaddress
from logging import Logger
from tplinkrouterc6u.encryption import EncryptionWrapper
from tplinkrouterc6u.enum import Wifi
from tplinkrouterc6u.dataclass import Firmware, Status, Device, IPv4Reservation, IPv4DHCPLease, IPv4Status


class TplinkRouter:
    def __init__(self, host: str, password: str, username: str = 'admin', logger: Logger = None,
                 verify_ssl: bool = True, timeout: int = 10) -> None:
        self.host = host
        if not (self.host.startswith('http://') or self.host.startswith('https://')):
            self.host = "http://{}".format(self.host)
        self._verify_ssl = verify_ssl
        if self._verify_ssl is False:
            requests.packages.urllib3.disable_warnings()
        self.username = username
        self.password = password
        self.timeout = timeout
        self.single_request_mode = True
        self._logger = logger

        self._stok = ''
        self._sysauth = ''

        self._logged = False
        self._seq = ''
        self._hash = hashlib.md5((self.username + self.password).encode()).hexdigest()

        self.nn = ''
        self.ee = ''

        self._pwdNN = ''
        self._pwdEE = ''

        self._encryption = EncryptionWrapper()

    def get_firmware(self) -> Firmware | None:
        return self._request(self._get_firmware)

    def get_status(self) -> Status | None:
        return self._request(self._get_status)

    def get_ipv4_status(self) -> IPv4Status | None:
        return self._request(self._get_ipv4_status)

    def get_ipv4_reservations(self) -> [IPv4Reservation]:
        return self._request(self._get_ipv4_reservations)

    def get_ipv4_dhcp_leases(self) -> [IPv4DHCPLease]:
        return self._request(self._get_ipv4_dhcp_leases)

    def query(self, query, operation='operation=read'):
        def callback():
            return self._get_data(query, operation)

        return self._request(callback)

    def get_full_info(self) -> tuple[Firmware, Status] | None:
        def callback():
            firmware = self._get_firmware()
            status = self._get_status()

            return firmware, status

        return self._request(callback)

    def set_wifi(self, wifi: Wifi, enable: bool) -> None:
        def callback():
            path = f"admin/wireless?&form=guest&form={wifi.value}"
            data = f"operation=write&{wifi.value}_enable={'on' if enable else 'off'}"
            self._send_data(path, data)

        self._request(callback)

    def reboot(self) -> None:
        def callback():
            self._send_data('admin/system?form=reboot', 'operation=write')

        self._request(callback)

    def authorize(self) -> bool:
        referer = '{}/webpages/login.html?t=1596185370610'.format(self.host)

        #if self._pwdNN == '':
        #    self._request_pwd(referer)

        #if self._seq == '':
        #    self._request_seq(referer)

        response = self._try_login(referer)

        #if 'text/plain' in response.headers.get('Content-Type'):
        #    self._request_pwd(referer)
        #    self._request_seq(referer)
        #    response = self._try_login(referer)

        try:
            #jsonData = response.json()

            #if 'data' not in jsonData or not jsonData['data']:
            #    raise Exception('No data in response: ' + response.text)

            #encryptedResponseData = jsonData['data']
            #responseData = self._encryption.aes_decrypt(encryptedResponseData)

            #responseDict = json.loads(responseData)

            #if 'success' not in responseDict or not responseDict['success']:
            #    raise Exception('No data in response: ' + responseData)

            self._stok = response.json().get('data').get('stok')
            regex_result = re.search('sysauth=(.*);', response.headers['set-cookie'])
            self._sysauth = regex_result.group(1)
            self._logged = True
            return True
        except (ValueError, KeyError, AttributeError) as e:
            if self._logger:
                self._logger.error("TplinkRouter Integration Exception - Couldn't fetch auth tokens! Response was: %s",
                                   response.text)

        return False

    def logout(self) -> None:
        if self._logged:
            self._send_data('admin/system?form=logout', 'operation=write')
        self.clear()

    def clear(self) -> None:
        self._stok = ''
        self._sysauth = ''
        self._logged = False

    def _get_firmware(self) -> Firmware:
        data = self._get_data('admin/firmware?form=upgrade', 'operation=read')
        firmware = Firmware(data.get('hardware_version', ''), data.get('model', ''), data.get('firmware_version', ''))

        return firmware

    def _get_status(self) -> Status:

        def _calc_cpu_usage(data: dict) -> float | None:
            cpu_usage = (data.get('cpu_usage', 0) + data.get('cpu1_usage', 0)
                         + data.get('cpu2_usage', 0) + data.get('cpu3_usage', 0))
            return cpu_usage / 4 if cpu_usage != 0 else None

        data = self._get_data('admin/status?form=all', 'operation=read')
        status = Status()
        status.devices = []
        status._wan_macaddr = macaddress.EUI48(data['wan_macaddr']) if 'wan_macaddr' in data else None
        status._lan_macaddr = macaddress.EUI48(data['lan_macaddr'])
        status._wan_ipv4_addr = ipaddress.IPv4Address(data['wan_ipv4_ipaddr']) if 'wan_ipv4_ipaddr' in data else None
        status._lan_ipv4_addr = ipaddress.IPv4Address(data['lan_ipv4_ipaddr']) if 'lan_ipv4_ipaddr' in data else None
        status._wan_ipv4_gateway = ipaddress.IPv4Address(
            data['wan_ipv4_gateway']) if 'wan_ipv4_gateway' in data else None
        status.wan_ipv4_uptime = data.get('wan_ipv4_uptime')
        status.mem_usage = data.get('mem_usage')
        status.cpu_usage = _calc_cpu_usage(data)
        status.wired_total = len(data.get('access_devices_wired', []))
        status.wifi_clients_total = len(data.get('access_devices_wireless_host', []))
        status.guest_clients_total = len(data.get('access_devices_wireless_guest', []))
        status.clients_total = status.wired_total + status.wifi_clients_total + status.guest_clients_total
        status.guest_2g_enable = data.get('guest_2g_enable') == 'on'
        status.guest_5g_enable = data.get('guest_5g_enable') == 'on'
        status.iot_2g_enable = data.get('iot_2g_enable') == 'on' if data.get('iot_2g_enable') is not None else None
        status.iot_5g_enable = data.get('iot_5g_enable') == 'on' if data.get('iot_5g_enable') is not None else None
        status.wifi_2g_enable = data.get('wireless_2g_enable') == 'on'
        status.wifi_5g_enable = data.get('wireless_5g_enable') == 'on'

        for item in data.get('access_devices_wireless_host', []):
            type = Wifi.WIFI_2G if '2.4G' == item['wire_type'] else Wifi.WIFI_5G
            status.devices.append(Device(type, macaddress.EUI48(item['macaddr']), ipaddress.IPv4Address(item['ipaddr']),
                                         item['hostname']))

        for item in data.get('access_devices_wireless_guest', []):
            type = Wifi.WIFI_GUEST_2G if '2.4G' == item['wire_type'] else Wifi.WIFI_GUEST_5G
            status.devices.append(Device(type, macaddress.EUI48(item['macaddr']), ipaddress.IPv4Address(item['ipaddr']),
                                         item['hostname']))

        return status

    def _get_ipv4_status(self) -> IPv4Status:
        ipv4_status = IPv4Status()
        data = self._get_data('admin/network?form=status_ipv4', 'operation=read')
        ipv4_status._wan_macaddr = macaddress.EUI48(data['wan_macaddr'])
        ipv4_status._wan_ipv4_ipaddr = ipaddress.IPv4Address(data['wan_ipv4_ipaddr'])
        ipv4_status._wan_ipv4_gateway = ipaddress.IPv4Address(data['wan_ipv4_gateway'])
        ipv4_status.wan_ipv4_conntype = data['wan_ipv4_conntype']
        ipv4_status._wan_ipv4_netmask = ipaddress.IPv4Address(data['wan_ipv4_netmask'])
        ipv4_status._wan_ipv4_pridns = ipaddress.IPv4Address(data['wan_ipv4_pridns'])
        ipv4_status._wan_ipv4_snddns = ipaddress.IPv4Address(data['wan_ipv4_snddns'])
        ipv4_status._lan_macaddr = macaddress.EUI48(data['lan_macaddr'])
        ipv4_status._lan_ipv4_ipaddr = ipaddress.IPv4Address(data['lan_ipv4_ipaddr'])
        ipv4_status.lan_ipv4_dhcp_enable = self._str2bool(data['lan_ipv4_dhcp_enable'])
        ipv4_status._lan_ipv4_netmask = ipaddress.IPv4Address(data['lan_ipv4_netmask'])
        ipv4_status.remote = self._str2bool(data['remote'])

        return ipv4_status

    def _get_ipv4_reservations(self) -> [IPv4Reservation]:
        ipv4_reservations = []
        data = self._get_data('admin/dhcps?form=reservation', 'operation=load')

        for item in data:
            ipv4_reservations.append(
                IPv4Reservation(macaddress.EUI48(item['mac']), ipaddress.IPv4Address(item['ip']), item['comment'],
                                self._str2bool(item['enable'])))

        return ipv4_reservations

    def _get_ipv4_dhcp_leases(self) -> [IPv4DHCPLease]:
        dhcp_leases = []
        data = self._get_data('admin/dhcps?form=client', 'operation=load')

        for item in data:
            dhcp_leases.append(
                IPv4DHCPLease(macaddress.EUI48(item['macaddr']), ipaddress.IPv4Address(item['ipaddr']), item['name'],
                              item['leasetime']))

        return dhcp_leases

    def _query(self, query, operation):
        data = self._get_data(query, operation)

        # for item in data:
        #    dhcp_leases.append(IPv4DHCPLease(macaddress.EUI48(item['macaddr']), ipaddress.IPv4Address(item['ipaddr']), item['name'], item['leasetime']))

        return data

    # TODO
    #        data2 = self._get_data('admin/dhcps?form=setting', 'operation=read')

    def _str2bool(self, v):
        return str(v).lower() in ("yes", "true", "on")

    def _request_pwd(self, referer: str) -> None:
        url = '{}/cgi-bin/luci/;stok=/login?form=keys'.format(self.host)

        # If possible implement RSA encryption of password here.
        response = requests.post(
            url, params={'operation': 'read'},
            headers={'Referer': referer},
            timeout=self.timeout,
            verify=self._verify_ssl,
        )

        try:
            data = response.json()

            args = data['data']['password']

            self._pwdNN = args[0]
            self._pwdEE = args[1]
        except json.decoder.JSONDecodeError:
            if self._logger:
                self._logger.error('TplinkRouter Integration Exception - No pwd response - {}'.format(response.text))
            raise Exception('Unsupported router!')
        except Exception as error:
            raise Exception('Unknown error for pwd - {}; Response - {}'.format(error, response.text))

    def _request_seq(self, referer: str) -> None:
        url = '{}/cgi-bin/luci/;stok=/login?form=auth'.format(self.host)

        # If possible implement RSA encryption of password here.
        response = requests.post(
            url,
            params={'operation': 'read'},
            headers={'Referer': referer},
            timeout=self.timeout,
            verify=self._verify_ssl,
        )

        try:
            data = response.json()

            self._seq = data['data']['seq']
            args = data['data']['key']

            self.nn = args[0]
            self.ee = args[1]
        except json.decoder.JSONDecodeError:
            if self._logger:
                self._logger.error('TplinkRouter Integration Exception - No seq response - {}'.format(response.text))
            raise Exception('Unsupported router!')
        except Exception as error:
            raise Exception('Unknown error for seq - {}; Response - {}'.format(error, response.text))

    def _try_login(self, referer: str) -> requests.Response:
        url = '{}/cgi-bin/luci/;stok=/login?form=login'.format(self.host)

        #cryptedPwd = self._encryption.rsa_encrypt(self.password, self._pwdNN, self._pwdEE)
        #data = 'operation=login&password={}&confirm=true'.format(cryptedPwd)

        #body = self._prepare_data(data)

        return requests.post(
            url,
            params={'operation': 'login', 'username': 'admin', 'password': 'add password here'},
            headers={'Referer': referer, 'Content-Type': 'application/x-www-form-urlencoded'},
            timeout=self.timeout,
            #verify=self._verify_ssl,
        )

    def _prepare_data(self, data) -> dict:
        encrypted_data = self._encryption.aes_encrypt(data)
        data_len = len(encrypted_data)

        sign = self._encryption.get_signature(int(self._seq) + data_len, self._logged == False, self._hash, self.nn,
                                              self.ee)

        return {'sign': sign, 'data': encrypted_data}

    def _request(self, callback: Callable):
        if not self.single_request_mode:
            return callback()

        try:
            if self.authorize():
                data = callback()
                self.logout()
                return data
        except Exception as error:
            self._seq = ''
            self._pwdNN = ''
            if self._logger:
                self._logger.error('TplinkRouter Integration Exception - {}'.format(error))
        finally:
            self.clear()

    def _get_data(self, path: str, data: str = 'operation=read') -> dict | None:
        if self._logged is False:
            raise Exception('Not authorised')
        url = '{}/cgi-bin/luci/;stok={}/{}'.format(self.host, self._stok, path)
        referer = '{}/webpages/index.html'.format(self.host)

        response = requests.post(
            url,
            data=data,
            headers={'Referer': referer},
            cookies={'sysauth': self._sysauth},
            timeout=self.timeout,
            verify=self._verify_ssl,
        )

        data = response.text
        print(data)
        try:
            json_response = response.json()
            if 'data' not in json_response:
                raise Exception("Router didn't respond with JSON - " + data)
            #data = self._encryption.aes_decrypt(json_response['data'])

            json_response = json.loads(data)

            if 'success' in json_response and json_response['success']:
                return json_response['data']
            else:
                if 'errorcode' in json_response and json_response['errorcode'] == 'timeout':
                    if self._logger:
                        self._logger.info(
                            "TplinkRouter Integration Exception - Token timed out. Relogging on next scan")
                    self._stok = ''
                    self._sysauth = ''
                elif self._logger:
                    self._logger.error(
                        "TplinkRouter Integration Exception - An unknown error happened while fetching data %s", data)
        except ValueError:
            if self._logger:
                self._logger.error(
                    "TplinkRouter Integration Exception - Router didn't respond with JSON. Check if credentials are correct")

        raise Exception('An unknown response - ' + data)

    def _send_data(self, path: str, data: str) -> None:
        if self._logged is False:
            raise Exception('Not authorised')
        url = '{}/cgi-bin/luci/;stok={}/{}'.format(self.host, self._stok, path)
        referer = '{}/webpages/index.1596185370610.html'.format(self.host)

        body = data
        requests.post(
            url,
            data=body,
            headers={'Referer': referer, 'Content-Type': 'application/x-www-form-urlencoded'},
            cookies={'sysauth': self._sysauth},
            timeout=self.timeout,
            verify=self._verify_ssl,
        )

this is some other code i used for testing:

import re

# Settings for TPLink
url = 'http://192.168.0.1/cgi-bin/luci/;stok=/login?form=login'
urlbegin = 'http://192.168.0.1/cgi-bin/luci/;stok='
urlstateind = '/admin/wireless?form=statistics'
urlloeind = '/admin/system?form=logout'

referer = 'http://192.168.0.1/webpages/login.html'

# Credentials TP link
admin = 'admin'
# Encrypted password see https://www.home-assistant.io/components/device_tracker.tplink/
pwd = ''


# Retrieve auth tokens TPlink
stok = ''
sysauth = ''

# If possible, implement RSA encryption of password here.
response = requests.post(url, params={'operation': 'login',
                                      'username': admin, 'password': pwd},
                         headers={'Referer': referer}, timeout=4)

try:
    stok = response.json().get('data').get('stok')
    regex_result = re.search('sysauth=(.*);', response.headers['set-cookie'])
    sysauth = regex_result.group(1)

    urlstat = urlbegin + stok + urlstateind
    response = requests.post(urlstat, params={'operation': 'load'},
                             headers={'Referer': referer},
                             cookies={'sysauth': sysauth}, timeout=5)

    responsstat = re.findall(r'mac":".................', response.text)
    mac_addresses = [mac_address.replace('mac":"', '') for mac_address in responsstat]

    print(mac_addresses)
    goon = 1

    urllo = urlbegin + stok + urlloeind
    response = requests.post(urllo, params={'operation': 'write'},
                              headers={'Referer': referer},
                              cookies={'sysauth': sysauth}, timeout=5)

    goon = 1
except (ValueError, KeyError) as _:
    print("Couldn't fetch auth tokens! Response was: %s", response.text)
    goon = 0

if goon == 0:
    print("quit")
    quit()

[AC1200/Archer A5] Support Data

AC12000WirelessDualBandRouter.zip

--> "Connexion" (arrived in Status menu)
image

--> "Wireless 2.4GHz" (arrived in Basic Settings) ->"Disable" then "Enable"
image

--> "Wireless 5GHz" (arrived in Basic Settings) -> "Disable" then "Enable"
image

--> "System Tools" (arrived in Time Settings)
image

--> "Logout"
image

--> "Connexion" (arrived in Status menu)
--> "Guest Network" -> "Disable" then "Enable"
image

--> "System Tools" (arrived in Time Settings) -> "Reboot" -> "Reboot"
image

If anyone can help to support this model. If there is any additionnal diagnostic to do leave a comment, i'll try my best to help

Reading received SMS

I'm deciding on what Router to buy and I need the capacity to retrieve existing SMS. I saw in the code that it can send but didn't find the API to retrieve received messages.

Is there something blocking the implementation or it was just not yet implemented?

If it's the latter I can take a look when I buy one (thinking on Archer MR600 V3).

How to obtain 5Ghz network connected devices using this python api?

I am able to use the python script to get all the 2.4Ghz network connected devices list using the included script however I am not sure how I can also request the 5Ghz connected devices or if I need a separate script to run for them?
I am running the script on an Archer AX73 TP-Link AX5400 Version 2.0 Firmware 1.1.0.


from tplinkrouterc6u import (
    TplinkRouterProvider,
    TplinkRouter,
    TplinkC1200Router,
    TPLinkMRClient,
    TPLinkDecoClient,
    Connection
)
from logging import Logger

router = TplinkRouterProvider.get_client('http://192.168.0.1', 'mypassword123here')
# You may use client directly like
# router = TplinkRouter('http://192.168.0.1', 'password')
# You may also pass username if it is different and a logger to log errors as
# router = TplinkRouter('http://192.168.0.1','password','admin2', Logger('test'))
# If you have the TP-link C1200 V2 or similar, you can use the TplinkC1200Router class instead of the TplinkRouter class.
# Remember that the password for this router is different, here you need to use the web encrypted password.
# To get web encrypted password, read Web Encrypted Password section
# router = TplinkC1200Router('http://192.168.0.1','WebEncryptedPassword', Logger('test'))

try:
    router.authorize()  # authorizing
    # Get firmware info - returns Firmware
    firmware = router.get_firmware()

    # Get status info - returns Status
    status = router.get_status()
   # if not status.guest_2g_enable:  # check if guest 2.4G wifi is disable
    # router.set_wifi(Connection.GUEST_2G, True)  # turn on guest 2.4G wifi

    # Get Address reservations, sort by ipaddr
    reservations = router.get_ipv4_reservations()
    reservations.sort(key=lambda a: a.ipaddr)
    for res in reservations:
        print(f"{res.macaddr} {res.ipaddr:16s} {res.hostname:36} {'Permanent':12}")

    # Get DHCP leases, sort by ipaddr
    leases = router.get_ipv4_dhcp_leases()
    leases.sort(key=lambda a: a.ipaddr)
    for lease in leases:
        print(f"{lease.macaddr} {lease.ipaddr:16s} {lease.hostname:36} {lease.lease_time:12}")
finally:
    router.logout()  # always logout as TP-Link Web Interface only supports upto 1 user logged

input("Enter any key to quit.") 
# sys.exit() is used to make the program quits. ( duh ) 
sys.exit() 

JSON Parse error

Hi, im getting a json parse error even though the response value seems to be valid json. Any idea?

response.text output:
{"data":"V91SCKGNBUpSyV0vhtTUTxSraG96i9gpP/UHARnmoDmQf+DJvKlv1jME/Bw164Aadg3VhU5ErvX7RhGqJ6cx6OkVPxjF0RtWiKOdK/U988g="}

TplinkRouter - C6 - Cannot authorize! Error - Expecting value: line 1 column 1 (char 0); Response -
Traceback (most recent call last):
File "/Users/faye.schipper/.pyenv/versions/3.10.8/lib/python3.10/site-packages/requests/models.py", line 971, in json
return complexjson.loads(self.text, **kwargs)
File "/Users/faye.schipper/.pyenv/versions/3.10.8/lib/python3.10/json/init.py", line 346, in loads
return _default_decoder.decode(s)
File "/Users/faye.schipper/.pyenv/versions/3.10.8/lib/python3.10/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/Users/faye.schipper/.pyenv/versions/3.10.8/lib/python3.10/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Users/faye.schipper/Downloads/TP-Link-Archer-C6U-main/tplinkrouterc6u/client.py", line 268, in authorize
data = response.json()
File "/Users/faye.schipper/.pyenv/versions/3.10.8/lib/python3.10/site-packages/requests/models.py", line 975, in json
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Tp-Link N600 (TL-WDR3600) Support Data

Firmware Version: 3.13.34 Build 130909 Rel.53148n
Hardware Version: WDR3600 v1 00000000
Device Info: https://www.tp-link.com/es/home-networking/wifi-router/tl-wdr3600/

(Sorry, I'm unable to find the device info page in english)

-Status (landing page)
-Quick setup
-Network
-Network/WAN
-Network/LAN
-Network/MAC Clone
-Dual Band Selection
-Wireless 2.4Ghz
-Wireless 2.4Ghz/Wireless Settings
-Wireless 2.4Ghz/WPS
-Wireless 2.4Ghz/Wireless Security
-Wireless 2.4Ghz/Wireless Mac Filtering
-Wireless 2.4Ghz/Wireless Advanced
-Wireless 2.4Ghz/Wireless Statistics
-Wireless 5Ghz
-Wireless 5Ghz/Wireless Settings
-Wireless 5Ghz/WPS
-Wireless 5Ghz/Wireless Security
-Wireless 5Ghz/Wireless Mac Filtering
-Wireless 5Ghz/Wireless Advanced
-Wireless 5Ghz/Wireless Statistics
-Guest Network
-Guest Network/wireless settings
-Guest Network/Storage Sharing
-DHCP
-DHCP/DHCP settings
-DHCP/DHCP Client List
-DHCP/Address Reservation
-USB Settings
-USB Settings/Storage Sharing
-USB Settings/FTP Server
-USB Settings/Media Server
-USB Settings/Print Server
-USB Settings/User Accounts
-NAT
-Forwarding
-Forwarding/Virtual Servers
-Forwarding/Port Triggering
-Forwarding/DMZ
-Forwarding/UPnP
-Security
-Security/Basic Security
-Security/Advanced Security
-Security/Local Management
-Security/Remote Management
-Parental Control
-Access Control/Rule
-Access Control/Host
-Access Control/Target
-Access Control/Schedule
-Advanced Routing
-Advanced Routing/Static Routing Table
-Advanced Routing/System Routing Table
-Bandwidth Control
-Bandwidth Control/Control Settings
-Bandwidth Control/Rules List
-IP & MAC Binding
-IP & MAC Binding/Binding Settings
-IP & MAC Binding/ARP List
-Dynamic DNS
-IPv6 Support
-IPv6 Support/IPv6 Status
-IPv6 Support/IPv6 Setup
-System Tools
-System Tools/Time Settings
-System Tools/Diagnostic
-System Tools/Firmware upgrade
-System Tools/Factory Defaults
-System Tools/Backup & Restore
-System Tools/Reboot
-System Tools/Password
-System Tools/System Log
-System Tools/Statistics

TL-WDR3600-har.zip

tp-link VX220-G2v Support Data

tp-link VX220-G2v Support Data
Firmware Version: 2.0.0 0.9 v603c.0 Build 220517 Rel.49186n
Hardware Version: VX220-G2v v2.0 00000000
Device Info - https://www.tp-link.com/au/service-provider/xdsl/vx220-g2v/

Description:
Login
Wireless Clients
Wired Clients
Phone
USB Disk
Internet
Wireless -> Wireless Settings enable
System Tools -> Traffic Statistics
Firmware Upgrade
System Log
Log out
Reboot

tp-link VX220-G2v.har.zip

Please let me know if there is anything else you need!
Thanks!

authorize() return value

Hello, this is great work, I've been looking for something like this for a while and I'm really glad I found this.

I just have a quick question. I was initially using the example code in the README and it was not working. I eventually discovered that was because the authorize() function was returning None, which I then learned Python interprets as False, so the entire if branch was not being traversed at all. I looked at the declaration for authorize() and surely enough type hint is None, and I didn't see a return anywhere inside the function. I eventually managed to get it working on my AX55 by removing the if.

I just wanted to know if this was intentional? My experience with Python is not very advanced so I'm not sure if I'm just missing something.

Signal strength data

Hi,

I was wondering if it is possible to get the signal strength data of wifi devices somehow?

image

Test with archer C80

It is not working with Archer C80 firmware 1.13.2
There are no error messages

May be related to the fact that C80 doesn’t have username, it only prompts for password

Archer C80 Support Data

Hi,
I've done my best to follow the support instructions. I'd much appreciate if you are able to provide support for this model.

Thank you!
tar file with logs is attached.
192.168.0.2.har.tar.gz

Description of what I did:
0) log in

  1. Open main page
  2. click on client list
  3. wireless settings
  4. enable 2.4 GHz, save. Disable 2.4 GHz, save
  5. disable 5 Ghz, save. enable 5 GHz, save
  6. Internet status page (mac, internet ip, subnet mask)
  7. Archer C80 status page (with quick toggles for wireless networks etc)
  8. toggle 2.4 Ghz network
  9. toggle 5 Ghz network
  10. toggle 2.4 GHz network (on)
  11. toggle 2.4 GHz guest network
  12. toggle 5 GHz guest network
  13. toggle 2.4 GHz IoT network
  14. toggle 5 GHz IoT network
  15. system reboot
  16. log in again
  17. log out

Shutdown?

It looks like it is not possible to use the API to shutdown the TP Link Router, why not?

Devices listed missing values

Hi,

I've been trying to get the download speed / upload speed for the devices connected to my router (Archer AX20) and when I iterated over the devices list, the results were always None on all values not provided in the parameters of Device (upload,download, pkt sent, pkt recieved)

The following code is reached in your client.py :
image
image

I fixed my issue with the upload/ download by moving the value assignments here:
image

Cheers 👍

Support for MLO

Hey thank you for this lib!

I tested it with a tp-link BE800 v1.0 and it's working as expected (POC). It can be added in the supported devices

It is missing support for MLO though, i checked the status values and this is what i see for it

{
    "mlo_host_5g_enable": "on",
    "mlo_host_6g_psk_key": "PSK_KEY_PSK_KEY",
    "mlo_host_6g_encryption": "psk_sae",
    "mlo_host_2g_hidden": "off",
    "mlo_host_2g_encryption": "psk_sae",
    "mlo_host_5g_ssid": "router_MLO",
    "mlo_host_2g_enable": "off",
    "mlo_host_6g_support": "on",
    "mlo_host_5g_encryption": "psk_sae",
    "mlo_host_6g_ssid": "router_MLO",
    "mlo_host_5g_psk_key": "PSK_KEY_PSK_KEY",
    "mlo_host_6g_enable": "on",
    "mlo_host_5g_support": "on",
    "mlo_host_2g_band_select": "off",
    "mlo_host_5g_hidden": "off",
    "mlo_host_2g_psk_key": "PSK_KEY_PSK_KEY",
    "mlo_host_6g_psk_version": "sae_only",
    "mlo_host_6g_band_select": "on",
    "mlo_host_2g_ssid": "router_MLO",
    "mlo_host_5g_band_select": "on",
    "mlo_host_2g_support": "on",
    "mlo_host_5g_psk_version": "sae_only",
    "mlo_host_2g_psk_version": "sae_only",
    "mlo_host_6g_hidden": "off"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.