Skip to main content

tplinkctl

CI PyPI

Tiny, scriptable CLI for a local TP-Link router admin page such as http://192.168.0.1/webpages/index.html#/internetAdv.

This wrapper uses tplinkrouterc6u for TP-Link's login encryption/session flow. The current live target identifies as Archer BE3500 v1.0; its unauthenticated UI bundle retains a BE220v1 build identifier.

Why

Router admin pages are slow, stateful, and annoying to automate. tplinkctl turns the useful parts into a local-first command line:

  • JSON-first output for jq, agents, and scripts
  • Safe defaults: no password stored, no overlapping sessions, dangerous commands require confirmation
  • Real router UI discovery: extract routes and API endpoints from TP-Link's bundled JavaScript
  • Escape hatches: read and raw for endpoint experiments

Install

Use Python 3.10+.

pip install tplink-admin-cli

Or from source:

python3 -m venv .venv
.venv/bin/python -m pip install .

Quick Start

tplinkctl config set --host http://192.168.0.1 --username admin --client sg
export TPLINK_PASSWORD='your-local-router-password'

tplinkctl doctor
tplinkctl --json capabilities
tplinkctl --json tools
tplinkctl --json demo
tplinkctl-mcp
tplinkctl --json events --tail 10
tplinkctl --json state save --name before-change
tplinkctl --json doctor --deep
tplinkctl --json health
tplinkctl --json status
tplinkctl --json led status
tplinkctl --json led off --plan
tplinkctl --json led schedule on --start 23:00 --end 07:00 --plan
tplinkctl devices --active
tplinkctl device Pixel
tplinkctl device reserve Pixel --yes
tplinkctl device block Pixel --yes --enforce
tplinkctl speed
tplinkctl wifi-info
tplinkctl speedtest --skip-upload
tplinkctl --json wan
tplinkctl --json firmware
tplinkctl --json firmware-check

Prefer TPLINK_PASSWORD or the interactive password prompt over --password, so the password is not left in shell history.

Commands

tplinkctl firmware
tplinkctl firmware-check
tplinkctl led status
tplinkctl led on --yes
tplinkctl led off --yes
tplinkctl led schedule on --start 23:00 --end 07:00 --yes
tplinkctl capabilities
tplinkctl tools
tplinkctl demo
tplinkctl events --tail 20
tplinkctl state save --name baseline
tplinkctl state diff
tplinkctl health
tplinkctl status
tplinkctl snapshot
tplinkctl wan
tplinkctl devices --active --sort usage
tplinkctl devices --connection host_5g --sort signal
tplinkctl device 192.168.0.40
tplinkctl device show Pixel
tplinkctl device access status
tplinkctl device access on --mode black --yes
tplinkctl device reserve Pixel --ip 192.168.0.40 --yes
tplinkctl device release Pixel --yes
tplinkctl device block Pixel --yes --enforce
tplinkctl device unblock Pixel --yes
tplinkctl device vpn Pixel on --yes
tplinkctl speed --top 10
tplinkctl watch devices --count 3 --active
tplinkctl speedtest
tplinkctl wifi-info
tplinkctl wifi-info --group guest
tplinkctl wifi-status
tplinkctl ipv4
tplinkctl leases
tplinkctl reservations
tplinkctl clients --active
tplinkctl wifi guest_2g on
tplinkctl wifi guest_2g off
tplinkctl wifi-config host_5g --channel 149 --width 80 --plan
tplinkctl wifi-config host_5g --channel 149 --width 80 --yes
tplinkctl vpn-status
tplinkctl vpn-client-status
tplinkctl reboot --yes

Device management commands use the same hostname/IP/MAC lookup as tplinkctl device Pixel. Mutating commands require --yes. device block --enforce also enables Access Control and switches the router into blacklist mode, so use device access status first if you want to inspect the current policy before enforcing it.

Config

tplinkctl config path
tplinkctl config show
tplinkctl config set --host http://192.168.0.1 --client sg --timeout 30

Config stores non-secret defaults in ~/.config/tplink-admin/config.json. Environment variables override config:

TPLINK_HOST=http://192.168.0.1
TPLINK_USERNAME=admin
TPLINK_CLIENT=sg
TPLINK_TIMEOUT=30
TPLINK_PASSWORD=...
TPLINK_JSON=1
TPLINK_ENABLE_COMMANDS=status,clients,leases
TPLINK_DISABLE_COMMANDS=reboot,wifi

Agent-Friendly Use

Data goes to stdout. Use --json for machine-readable output and --no-input when an agent should fail instead of prompting:

tplinkctl --json --no-input health
tplinkctl --json --no-input status | jq '.router, .wan, .wifi.networks, .devices[] | {hostname, ip, connection, active, down, up, usage}'
tplinkctl --json --enable-commands status,devices devices --active
tplinkctl --disable-commands reboot,wifi status

Use policy profiles when a whole agent session needs a fixed permission envelope:

tplinkctl --json --profile read-only status
tplinkctl --json --profile device-admin device block Pixel --plan --enforce
tplinkctl --json --profile device-admin device block Pixel --yes --enforce
tplinkctl --json --profile network-admin wifi guest_2g on

Profiles can also be set with TPLINK_PROFILE=read-only, device-admin, network-admin, or dangerous.

Plan mutating device operations before executing them:

tplinkctl --json --reason "pin phone IP" --no-input device reserve Pixel --plan
tplinkctl --json --reason "pause phone network access" --no-input device block Pixel --plan --enforce
tplinkctl --json --no-input device unblock Pixel --plan

Plans and mutations are appended to ~/.config/tplink-admin/events.jsonl:

tplinkctl --json events --tail 20
tplinkctl --json events --operation device.block

Save and compare redacted router state snapshots:

tplinkctl --json --no-input state save --name before
tplinkctl --json --no-input state save --name after
tplinkctl --json state show --list
tplinkctl --json state diff --before before --after after

By default state diff filters rate/counter/timestamp noise (e.g. online_seconds, packets_received, cpu_usage, signal_dbm) so a no-op diff returns zero changes and a real mutation surfaces cleanly. Pass --raw to see every field, --only <prefix> to scope to a subtree (e.g. --only wifi), or --ignore <leaf-or-path> to skip a bare leaf name (signal_dbm) or a dotted/indexed prefix (devices[3]).

tools prints a local tool schema that agent frameworks can map to shell commands:

tplinkctl --json tools | jq '.tools[] | {name, read_only, command}'

watch samples read-only router state repeatedly. Use --stream for JSON Lines:

tplinkctl --json --no-input watch devices --active --count 5 --interval 2
tplinkctl --no-input watch speed --count 10 --interval 1 --stream

demo prints a safe workflow report for presentations and agent smoke tests:

tplinkctl --json demo
tplinkctl --json --no-input demo --live --device Pixel --save-state --state-name demo

See examples/agent-runbook.md and examples/transcripts for a redacted demo story.

doctor checks that the router web UI is reachable and reports page metadata without logging in:

tplinkctl --json doctor

Use doctor --deep when an agent needs a read-only authenticated readiness check across the core router endpoints:

tplinkctl --json --no-input doctor --deep

capabilities prints a stable agent-readable manifest with command IDs, risk levels, confirmation requirements, rollback hints, and known firmware quirks:

tplinkctl --json capabilities | jq '.capabilities[] | {id, command, status, risk}'

Authenticated commands are serialized with a local lock by default because the router login flow can reject overlapping sessions. Use --no-lock only when you are deliberately testing concurrency.

See AGENTS.md and llms.txt for the agent playbook and discovery entrypoint.

MCP Server

tplinkctl-mcp starts a small stdio JSON-RPC tool server for local agents. It exposes the same guarded operations as the CLI and uses TPLINK_MCP_PROFILE or TPLINK_PROFILE for policy enforcement:

export TPLINK_PASSWORD='your-local-router-password'
export TPLINK_MCP_PROFILE=device-admin
tplinkctl-mcp

Available tool methods include router_status, firmware_audit, led_status, led_plan, led_set, device_list, device_show, device_plan, device_block, device_unblock, doctor_deep, watch, audit_tail, state_snapshot, and state_diff. Mutating MCP tools require confirm=true in addition to the CLI's router-side safeguards.

The server speaks the core JSON-RPC methods used by MCP clients:

initialize
tools/list
tools/call
ping

See docs/INSTALL_AGENT.md for Codex/OpenClaw/Hermes-style setup notes and MCP config examples.

UI Discovery

Map router pages and mine API-looking endpoints from downloaded bundles:

scripts/mirror_ui.py --host http://192.168.0.1 --out .
tplinkctl --json routes --name internet
tplinkctl --json endpoints --form network

On this router, #/internetAdv maps to index-BOBVatjl.js, which exposes internet-related endpoints like port speed, WAN flow control, and IPv4 status. See docs/ENDPOINTS.md.

Raw Mode

The raw command is for experiments with endpoint paths discovered from the router's JavaScript bundle. Note that operation placement matters — some endpoints require operation=read in the URL path, others in the POST body:

# operation in path (wan_fc style)
tplinkctl raw '/admin/network?form=wan_fc&operation=read' --data ''

# operation in body (status_ipv4 style)
tplinkctl raw '/admin/network?form=status_ipv4' --data 'operation=read'

# see error responses without crashing
tplinkctl raw '/admin/path?form=form' --data 'operation=read' --ignore-errors

Quote endpoint paths in shells like zsh, because ? is a glob character.

Development

make test
make smoke
make demo
make agent-doctor
make doctor

Prior Art

The project shape follows the “small sharp CLI” style used across Peter Steinberger's tools: short command name, direct install path, config/env support, scriptable stdout, agent-safe flags, and an escape hatch for power users.

Relevant TP-Link libraries/projects:

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tplink_admin_cli-0.5.0.tar.gz (60.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tplink_admin_cli-0.5.0-py3-none-any.whl (45.6 kB view details)

Uploaded Python 3

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page