# TP-Link extenders

The `tplink` plugin logs in to TP-Link range extenders and access points through their web interface and reads the uplink, radio and client state. These devices have no SNMP. Turn it on when you run TP-Link extenders, because an extender that has lost its uplink still answers ping while serving nobody, and only this plugin sees that.

The code targets the RE505X and siblings that run TP-Link's LuCI-based firmware. 
## Enable it

Add `tplink` to the `plugins` array.

```json
{
  "plugins": ["tplink"],
  "tplink": {
    "refreshInterval": 120000,
    "timeout": 8000,
    "devices": [
      { "name": "upstairs-extender", "ip": "192.0.2.41", "password": "YOUR_ADMIN_PASSWORD" },
      { "name": "warehouse-extender", "ip": "192.0.2.42", "password": "YOUR_ADMIN_PASSWORD" }
    ]
  }
}
```

| Option | Default | What it does |
| --- | --- | --- |
| `refreshInterval` | `120000` | Milliseconds between polls. |
| `timeout` | `8000` | Milliseconds per HTTP request. |
| `devices` | `[]` | Devices to poll. An entry missing `ip` or `password` is skipped without a message. |
| `devices[].name` | the IP | Display name. |
| `devices[].ip` | required | Device address. |
| `devices[].password` | required | The device's web admin password. |
| `devices[].uniqueId` | `tplink-<name>` | Unique id the plugin sends with the report. The server files the report under the device's IP, so this rarely matters. |

## What it reports

One payload per device:

- `name`, `ip`, `kind: "extender"`, `reachable`.
- `internetStatus`, the raw uplink state, and `uplinkUp`, true when it is `connected`.
- `phyconn`.
- `radio2g`, `radio5g`, true when that band is switched on.
- `clientCount` and `clients`, each with `name`, `mac` and `band`.
- `model`, `hardwareVersion`, `firmware`. A failed firmware read leaves these empty and does not fail the poll.
- `error` with `reachable: false` when login or the status read fails.

## When it alerts

The server checks these rules in order and the first match sets the status:

| Severity | Trigger |
| --- | --- |
| error | The payload has `error`. |
| error | `reachable` is false. |
| error | `uplinkUp` is false. Reason "uplink &lt;state>, extender is not serving traffic". |
| error | Both radios are off. |
| warning | One radio is off. The reason names the band. |

TP-Link reports do not get their own service. The server folds them into a `device` entity keyed by IP, `dev-<ip>`, with any [netscan](./netscan.md) or [SNMP](./snmp.md) report for the same address. The device status is the worst across its sources. Device alerts go out on the first bad report under source `device`, and the server does not resolve them when the device recovers. 
## On the dashboard

The device card is labelled "Extender · &lt;model>".

- UPLINK shows UP in green or DOWN in red. It shows a dash, uncolored, when the device is unreachable.
- RADIOS shows how many bands are on out of 2. Green at 2, amber at 1, red at 0.
- CLIENTS shows the connected client count.
- FIRMWARE shows the version, when known.
- CONNECTED CLIENTS lists up to 12 clients with band and MAC.

Add the extender to netscan as well to get a PING metric on the same card. A device with no report from any source for 300 seconds shows OFFLINE.

## Requirements

- HTTP access from the agent to each device. The plugin talks plain `http://<ip>`.
- The device's web admin password. The plugin encrypts it with the device's RSA key before sending, the same way the web login does.

## Gotchas

- The plugin keeps a session per device and reuses it. When a read fails it logs in again once. A poll that still fails drops the session, so the next poll starts with a fresh login.
- `radio5g` is false whenever the status response has no 5 GHz field. A single-band device, or firmware that names the field differently, will sit at warning with "5GHz radio off".
