├── .gitignore
├── .flake8
├── docs
└── media
│ ├── router.png
│ ├── sensors.png
│ ├── switches.png
│ └── config_flow.png
├── hacs.json
├── .github
├── workflows
│ ├── codechecker.yml
│ ├── hassfest.yaml
│ ├── hacs.yaml
│ └── release.yml
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── custom_components
└── tplink_router
│ ├── services.yaml
│ ├── const.py
│ ├── manifest.json
│ ├── translations
│ ├── ja.json
│ ├── cs.json
│ ├── es.json
│ ├── bg.json
│ ├── it.json
│ ├── pt.json
│ ├── en.json
│ ├── sv.json
│ ├── tr.json
│ ├── sk.json
│ ├── nl.json
│ └── fr.json
│ ├── strings.json
│ ├── button.py
│ ├── config_flow.py
│ ├── coordinator.py
│ ├── __init__.py
│ ├── device_tracker.py
│ ├── switch.py
│ └── sensor.py
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .git
2 | venv
3 | .idea
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 |
3 | max-line-length = 120
4 | exclude = .git,.github,docs,venv
5 |
--------------------------------------------------------------------------------
/docs/media/router.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexandrErohin/home-assistant-tplink-router/HEAD/docs/media/router.png
--------------------------------------------------------------------------------
/docs/media/sensors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexandrErohin/home-assistant-tplink-router/HEAD/docs/media/sensors.png
--------------------------------------------------------------------------------
/docs/media/switches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexandrErohin/home-assistant-tplink-router/HEAD/docs/media/switches.png
--------------------------------------------------------------------------------
/docs/media/config_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexandrErohin/home-assistant-tplink-router/HEAD/docs/media/config_flow.png
--------------------------------------------------------------------------------
/hacs.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TP-Link Router",
3 | "filename": "tplink_router.zip",
4 | "homeassistant": "2023.9.3",
5 | "hide_default_branch": true,
6 | "render_readme": true,
7 | "zip_release": true
8 | }
--------------------------------------------------------------------------------
/.github/workflows/codechecker.yml:
--------------------------------------------------------------------------------
1 | name: Code Checker
2 | on:
3 | pull_request:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | ci:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - name: Run flake8
13 | uses: py-actions/flake8@v2
--------------------------------------------------------------------------------
/custom_components/tplink_router/services.yaml:
--------------------------------------------------------------------------------
1 | send_sms:
2 | fields:
3 | number:
4 | required: true
5 | selector:
6 | text:
7 | text:
8 | required: true
9 | selector:
10 | text:
11 | device:
12 | required: true
13 | selector:
14 | device:
--------------------------------------------------------------------------------
/custom_components/tplink_router/const.py:
--------------------------------------------------------------------------------
1 | DEFAULT_NAME = "TP-Link Router"
2 | DOMAIN = "tplink_router"
3 | DEFAULT_USER = "admin"
4 | DEFAULT_HOST = "http://192.168.0.1"
5 |
6 | EVENT_NEW_DEVICE = f"{DOMAIN}_new_device"
7 | EVENT_ONLINE = f"{DOMAIN}_device_online"
8 | EVENT_OFFLINE = f"{DOMAIN}_device_offline"
9 | EVENT_NEW_SMS = f"{DOMAIN}_new_sms"
10 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "tplink_router",
3 | "name": "TP-Link Router",
4 | "codeowners": ["@AlexandrErohin"],
5 | "config_flow": true,
6 | "documentation": "https://github.com/AlexandrErohin/home-assistant-tplink-router",
7 | "iot_class": "local_polling",
8 | "issue_tracker": "https://github.com/AlexandrErohin/home-assistant-tplink-router/issues",
9 | "requirements": ["tplinkrouterc6u==5.12.1"],
10 | "version": "2.14.1"
11 | }
--------------------------------------------------------------------------------
/.github/workflows/hassfest.yaml:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | name: Validate with hassfest
4 |
5 | on:
6 | push:
7 | pull_request:
8 |
9 | jobs:
10 | validate:
11 | runs-on: "ubuntu-latest"
12 | steps:
13 | - uses: actions/checkout@v4
14 | name: Download repo
15 | with:
16 | fetch-depth: 0
17 | - uses: actions/setup-python@v4
18 | name: Setup Python
19 | with:
20 | python-version: '3.10.x'
21 | - uses: "actions/checkout@v4"
22 | - uses: home-assistant/actions/hassfest@master
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Logs**
14 | Add also all relevant logs from **Settings->System->Logs**.
15 |
16 | **Additional Information (please complete the following information)**
17 | - Router Hardware Version:
18 | - Router Firmware Version:
19 | - Home Assistant Version:
20 | - TP-Link Router Integration Component Version:
21 |
--------------------------------------------------------------------------------
/.github/workflows/hacs.yaml:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | name: Validate HACS
4 | on:
5 | push:
6 | pull_request:
7 | jobs:
8 | ci:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | name: Download repo
13 | with:
14 | fetch-depth: 0
15 |
16 | - uses: actions/setup-python@v4
17 | name: Setup Python
18 | with:
19 | python-version: '3.10.x'
20 |
21 | - uses: actions/cache@v4
22 | name: Cache
23 | with:
24 | path: |
25 | ~/.cache/pip
26 | key: custom-component-ci
27 |
28 | - name: HACS Action
29 | uses: hacs/action@main
30 | with:
31 | CATEGORY: integration
32 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "統合の設定",
6 | "data": {
7 | "host": "ホスト",
8 | "username": "ログイン",
9 | "password": "パスワード",
10 | "scan_interval": "更新のスキャン間隔(秒)",
11 | "verify_ssl": "https接続でSSLを検証する"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "ルーターのパラメーターを編集します。",
20 | "data": {
21 | "host": "ホスト",
22 | "username": "ログイン",
23 | "password": "パスワード",
24 | "scan_interval": "更新のスキャン間隔(秒)",
25 | "verify_ssl": "https接続でSSLを検証する"
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Nastavení integrace",
6 | "data": {
7 | "host": "Host",
8 | "username": "Login",
9 | "password": "Heslo",
10 | "scan_interval": "Interval skenování v sekundách",
11 | "verify_ssl": "Kontrolovat ssl pro HTTPS připojení"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Upravit parametry nastavení.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Login",
23 | "password": "Heslo",
24 | "scan_interval": "Interval skenování v sekundách",
25 | "verify_ssl": "Kontrolovat ssl pro HTTPS připojení"
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Configuración de la integración",
6 | "data": {
7 | "host": "Host",
8 | "username": "Usuario",
9 | "password": "Contraseña",
10 | "scan_interval": "Intervalo de escaneo para actualizaciones en segundos",
11 | "verify_ssl": "Verificar SSL para conexión https"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Editar parámetros del router.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Usuario",
23 | "password": "Contraseña",
24 | "scan_interval": "Intervalo de escaneo para actualizaciones en segundos",
25 | "verify_ssl": "Verificar SSL para conexión https"
26 | }
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Конфигурация на интеграцията",
6 | "data": {
7 | "host": "Хост",
8 | "username": "Потребителско име",
9 | "password": "Парола",
10 | "scan_interval": "Интервал за сканиране за актуализации в секунди",
11 | "verify_ssl": "Проверка на SSL за HTTPS връзка"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Редактиране на параметрите за рутера.",
20 | "data": {
21 | "host": "Хост",
22 | "username": "Потребителско име",
23 | "password": "Парола",
24 | "scan_interval": "Интервал за сканиране за актуализации в секунди",
25 | "verify_ssl": "Проверка на SSL за HTTPS връзка"
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Configurazione dell'integrazione",
6 | "data": {
7 | "host": "Host",
8 | "username": "Nome utente",
9 | "password": "Password",
10 | "scan_interval": "Intervallo di scansione per aggiornamenti in secondi",
11 | "verify_ssl": "Verifica SSL per la connessione https"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Modifica parametri del router",
20 | "data": {
21 | "host": "Host",
22 | "username": "Nome utente",
23 | "password": "Password",
24 | "scan_interval": "Intervallo di scansione per aggiornamenti in secondi",
25 | "verify_ssl": "Verifica SSL per la connessione https"
26 | }
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/pt.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Configuração da integração",
6 | "data": {
7 | "host": "Host",
8 | "username": "Utilizador",
9 | "password": "Palavra-passe",
10 | "scan_interval": "Intervalo de verificação para atualizações em segundos",
11 | "verify_ssl": "Verificar SSL para a conexão https"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Editar parâmetros do router.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Utilizador",
23 | "password": "Palavra-passe",
24 | "scan_interval": "Intervalo de verificação para atualizações em segundos",
25 | "verify_ssl": "Verificar SSL para a conexão https"
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: "Release"
2 |
3 | on:
4 | release:
5 | types:
6 | - "published"
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | release:
12 | name: "Release"
13 | runs-on: "ubuntu-latest"
14 | permissions:
15 | contents: write
16 | steps:
17 | - name: "Checkout the repository"
18 | uses: "actions/checkout@v4"
19 |
20 | - name: "Adjust version number"
21 | shell: "bash"
22 | run: |
23 | yq -i -o json '.version="${{ github.event.release.tag_name }}"' \
24 | "${{ github.workspace }}/custom_components/tplink_router/manifest.json"
25 |
26 | - name: "ZIP the integration directory"
27 | shell: "bash"
28 | run: |
29 | cd "${{ github.workspace }}/custom_components/tplink_router"
30 | zip tplink_router.zip -r ./
31 |
32 | - name: "Upload the ZIP file to the release"
33 | uses: softprops/action-gh-release@v2
34 | with:
35 | files: ${{ github.workspace }}/custom_components/tplink_router/tplink_router.zip
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 AlexandrErohin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Integration config",
6 | "data": {
7 | "host": "Host",
8 | "username": "Login",
9 | "password": "Password",
10 | "scan_interval": "Scan interval for updates in seconds",
11 | "verify_ssl": "Verify ssl for https connection"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Edit parameters for the router.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Login",
23 | "password": "Password",
24 | "scan_interval": "Scan interval for updates in seconds",
25 | "verify_ssl": "Verify ssl for https connection"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Send SMS",
33 | "description": "Send SMS",
34 | "fields": {
35 | "number": {
36 | "name": "Phone",
37 | "description": "Phone number"
38 | },
39 | "text": {
40 | "name": "Message",
41 | "description": "Text"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "Select the TP-Link router to send sms"
46 | }
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Integrationskonfiguration",
6 | "data": {
7 | "host": "Host",
8 | "username": "Login",
9 | "password": "Lösenord",
10 | "scan_interval": "Uppdateringsintervall i sekunder",
11 | "verify_ssl": "Verifiera ssl för https anslutning"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Ändra konfiguration för routern.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Login",
23 | "password": "Lösenord",
24 | "scan_interval": "Uppdateringsintervall i sekunder",
25 | "verify_ssl": "Verifiera ssl för https anslutning"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Skicka SMS",
33 | "description": "Skicka SMS",
34 | "fields": {
35 | "number": {
36 | "name": "Telefon",
37 | "description": "Telefonnummer"
38 | },
39 | "text": {
40 | "name": "Meddelande",
41 | "description": "Text"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "Välj TP-Link router för att skicka sms"
46 | }
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Entegrasyon ayarları",
6 | "data": {
7 | "host": "Host",
8 | "username": "Kullanıcı Adı",
9 | "password": "Parola",
10 | "scan_interval": "Güncellemeler için tarama sıklığı (saniye)",
11 | "verify_ssl": "HTTPS için SSL doğrula"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Router için parametreleri düzenle.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Kullanıcı Adı",
23 | "password": "Parola",
24 | "scan_interval": "Güncellemeler için tarama sıklığı (saniye)",
25 | "verify_ssl": "HTTPS için SSL doğrula"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "SMS gönder",
33 | "description": "SMS gönder",
34 | "fields": {
35 | "number": {
36 | "name": "Telefon",
37 | "description": "Telefon numarası"
38 | },
39 | "text": {
40 | "name": "Mesaj",
41 | "description": "Metin"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "SMS göndermek için TP-Link Router seçin"
46 | }
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Nastavenie integrácie",
6 | "data": {
7 | "host": "Hostiteľ",
8 | "username": "Meno",
9 | "password": "Heslo",
10 | "scan_interval": "Interval skenovania aktualizácií v sekundách",
11 | "verify_ssl": "Overte ssl pre pripojenie https"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Upravte parametre smerovača.",
20 | "data": {
21 | "host": "Hostiteľ",
22 | "username": "Meno",
23 | "password": "Heslo",
24 | "scan_interval": "Interval skenovania aktualizácií v sekundách",
25 | "verify_ssl": "Overte ssl pre pripojenie https"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Pošlite SMS",
33 | "description": "Pošlite SMS",
34 | "fields": {
35 | "number": {
36 | "name": "Phone",
37 | "description": "Telefónne číslo"
38 | },
39 | "text": {
40 | "name": "Správa",
41 | "description": "Text"
42 | },
43 | "device": {
44 | "name": "Smerovač",
45 | "description": "Vyberte smerovač TP-Link na odosielanie SMS"
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Integratie configuratie",
6 | "data": {
7 | "host": "Host",
8 | "username": "Gebruikersnaam",
9 | "password": "Wachtwoord",
10 | "scan_interval": "Scaninterval voor updates in seconden",
11 | "verify_ssl": "Verifieer SSL voor https-verbinding"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Parameters bewerken voor de router.",
20 | "data": {
21 | "host": "Host",
22 | "username": "Gebruikersnaam",
23 | "password": "Wachtwoord",
24 | "scan_interval": "Scaninterval voor updates in seconden",
25 | "verify_ssl": "Verifieer SSL voor https-verbinding"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Verzend SMS",
33 | "description": "Een SMS verzenden",
34 | "fields": {
35 | "number": {
36 | "name": "Telefoon",
37 | "description": "Telefoonnummer"
38 | },
39 | "text": {
40 | "name": "Bericht",
41 | "description": "Tekst"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "Selecteer de TP-Link-router om een sms te verzenden"
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/translations/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "step": {
4 | "user": {
5 | "description": "Configuration de l'intégration",
6 | "data": {
7 | "host": "Hôte",
8 | "username": "Utilisateur",
9 | "password": "Mot de passe",
10 | "scan_interval": "Intervalle d'analyse des mises à jour en secondes",
11 | "verify_ssl": "Vérifier SSL pour la connexion https"
12 | }
13 | }
14 | }
15 | },
16 | "options": {
17 | "step": {
18 | "init": {
19 | "description": "Modifier les paramètres du routeur.",
20 | "data": {
21 | "host": "Hôte",
22 | "username": "Utilisateur",
23 | "password": "Mot de passe",
24 | "scan_interval": "Intervalle d'analyse des mises à jour en secondes",
25 | "verify_ssl": "Vérifier SSL pour la connexion https"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Envoi SMS",
33 | "description": "Envoi SMS",
34 | "fields": {
35 | "number": {
36 | "name": "Téléphone",
37 | "description": "Numéro de téléphone"
38 | },
39 | "text": {
40 | "name": "Message",
41 | "description": "Text"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "Select the TP-Link router to send sms"
46 | }
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "TP-Link Router",
3 | "config": {
4 | "step": {
5 | "user": {
6 | "description": "description",
7 | "data": {
8 | "host": "[%key:common::config_flow::data::host%]",
9 | "username": "[%key:common::config_flow::data::username%]",
10 | "password": "[%key:common::config_flow::data::password%]",
11 | "scan_interval": "[%key:common::config_flow::data::scan_interval%]",
12 | "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
13 | }
14 | }
15 | }
16 | },
17 | "options": {
18 | "step": {
19 | "init": {
20 | "data": {
21 | "host": "[%key:common::config_flow::data::host%]",
22 | "username": "[%key:common::config_flow::data::username%]",
23 | "password": "[%key:common::config_flow::data::password%]",
24 | "scan_interval": "[%key:common::config_flow::data::scan_interval%]",
25 | "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
26 | }
27 | }
28 | }
29 | },
30 | "services": {
31 | "send_sms": {
32 | "name": "Send SMS",
33 | "description": "Send SMS",
34 | "fields": {
35 | "number": {
36 | "name": "Phone",
37 | "description": "Phone number"
38 | },
39 | "text": {
40 | "name": "Message",
41 | "description": "Text"
42 | },
43 | "device": {
44 | "name": "Router",
45 | "description": "Select the TP-Link router to send sms"
46 | }
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/custom_components/tplink_router/button.py:
--------------------------------------------------------------------------------
1 | """Component providing support for TPLinkRouter button entities."""
2 | from __future__ import annotations
3 |
4 | from collections.abc import Callable
5 | from dataclasses import dataclass
6 | from typing import Any
7 | from homeassistant.const import EntityCategory
8 | from homeassistant.components.button import (
9 | ButtonDeviceClass,
10 | ButtonEntity,
11 | ButtonEntityDescription,
12 | )
13 | from homeassistant.config_entries import ConfigEntry
14 | from homeassistant.core import HomeAssistant
15 | from homeassistant.helpers.entity_platform import AddEntitiesCallback
16 | from homeassistant.helpers.update_coordinator import CoordinatorEntity
17 | from .const import DOMAIN
18 | from .coordinator import TPLinkRouterCoordinator
19 |
20 |
21 | @dataclass
22 | class TPLinkRouterButtonEntityDescriptionMixin:
23 | method: Callable[[TPLinkRouterCoordinator], Any]
24 |
25 |
26 | @dataclass
27 | class TPLinkButtonEntityDescription(
28 | ButtonEntityDescription, TPLinkRouterButtonEntityDescriptionMixin
29 | ):
30 | """A class that describes button entities for the host."""
31 |
32 |
33 | BUTTON_TYPES = (
34 | TPLinkButtonEntityDescription(
35 | key="reboot",
36 | name="Reboot",
37 | device_class=ButtonDeviceClass.RESTART,
38 | entity_category=EntityCategory.CONFIG,
39 | method=lambda coordinator: coordinator.reboot(),
40 | ),
41 | )
42 |
43 |
44 | async def async_setup_entry(
45 | hass: HomeAssistant,
46 | entry: ConfigEntry,
47 | async_add_entities: AddEntitiesCallback,
48 | ) -> None:
49 | coordinator = hass.data[DOMAIN][entry.entry_id]
50 |
51 | buttons = []
52 |
53 | for description in BUTTON_TYPES:
54 | buttons.append(TPLinkRouterButtonEntity(coordinator, description))
55 | async_add_entities(buttons, False)
56 |
57 |
58 | class TPLinkRouterButtonEntity(CoordinatorEntity[TPLinkRouterCoordinator], ButtonEntity):
59 | entity_description: TPLinkButtonEntityDescription
60 |
61 | def __init__(
62 | self,
63 | coordinator: TPLinkRouterCoordinator,
64 | description: TPLinkButtonEntityDescription,
65 | ) -> None:
66 | super().__init__(coordinator)
67 |
68 | self._attr_device_info = coordinator.device_info
69 | self._attr_unique_id = f"{coordinator.unique_id}_{DOMAIN}_{description.key}"
70 | self.entity_description = description
71 |
72 | async def async_press(self) -> None:
73 | await self.entity_description.method(self.coordinator)
74 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/config_flow.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import voluptuous as vol
3 | from typing import Any
4 | from homeassistant import config_entries
5 | from homeassistant.core import callback
6 | import homeassistant.helpers.config_validation as cv
7 | from homeassistant.data_entry_flow import FlowResult
8 | from .const import DOMAIN, DEFAULT_USER, DEFAULT_HOST
9 | from .coordinator import TPLinkRouterCoordinator
10 | from homeassistant.const import (
11 | CONF_HOST,
12 | CONF_PASSWORD,
13 | CONF_USERNAME,
14 | CONF_SCAN_INTERVAL,
15 | CONF_VERIFY_SSL,
16 | )
17 |
18 | _LOGGER = logging.getLogger(__name__)
19 |
20 |
21 | class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
22 |
23 | async def async_step_user(self, user_input=None):
24 | """Handle the initial step."""
25 | errors = {}
26 | schema = vol.Schema(
27 | {
28 | vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
29 | vol.Required(CONF_PASSWORD): cv.string,
30 | vol.Required(CONF_SCAN_INTERVAL, default=30): int,
31 | vol.Required(CONF_VERIFY_SSL, default=True): cv.boolean,
32 | }
33 | )
34 | if user_input is not None:
35 | try:
36 | router = await TPLinkRouterCoordinator.get_client(
37 | hass=self.hass,
38 | host=user_input[CONF_HOST],
39 | password=user_input[CONF_PASSWORD],
40 | username=user_input.get(CONF_USERNAME, DEFAULT_USER),
41 | logger=_LOGGER,
42 | verify_ssl=user_input[CONF_VERIFY_SSL],
43 | )
44 | await self.hass.async_add_executor_job(router.authorize)
45 | return self.async_create_entry(title=user_input["host"], data=user_input)
46 | except Exception as error:
47 | _LOGGER.error('TplinkRouter Integration Exception - {}'.format(error))
48 | errors['base'] = str(error)
49 | schema = vol.Schema(
50 | {
51 | vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
52 | vol.Required(CONF_PASSWORD): cv.string,
53 | vol.Required(CONF_USERNAME, default=DEFAULT_USER): str,
54 | vol.Required(CONF_SCAN_INTERVAL, default=30): int,
55 | vol.Required(CONF_VERIFY_SSL, default=True): cv.boolean,
56 | }
57 | )
58 |
59 | return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
60 |
61 | @staticmethod
62 | @callback
63 | def async_get_options_flow(config_entry: config_entries.ConfigEntry) -> config_entries.OptionsFlow:
64 | return OptionsFlow(config_entry)
65 |
66 |
67 | class OptionsFlow(config_entries.OptionsFlowWithConfigEntry):
68 |
69 | async def async_step_init(self, user_input: dict[str, Any] | None = None) -> FlowResult:
70 | errors = {}
71 | data = user_input or self.config_entry.data
72 |
73 | if user_input is not None:
74 | try:
75 | router = await TPLinkRouterCoordinator.get_client(
76 | hass=self.hass,
77 | host=user_input[CONF_HOST],
78 | password=user_input[CONF_PASSWORD],
79 | username=user_input[CONF_USERNAME],
80 | logger=_LOGGER,
81 | verify_ssl=user_input[CONF_VERIFY_SSL],
82 | )
83 | await self.hass.async_add_executor_job(router.authorize)
84 | self.hass.config_entries.async_update_entry(self.config_entry, data=user_input)
85 | return self.async_create_entry(title=user_input["host"], data=user_input)
86 | except Exception as error:
87 | _LOGGER.error('TplinkRouter Integration Exception - {}'.format(error))
88 | errors['base'] = str(error)
89 |
90 | data_schema = vol.Schema({
91 | vol.Required(CONF_HOST, default=data.get(CONF_HOST)): cv.string,
92 | vol.Required(CONF_USERNAME, default=data.get(CONF_USERNAME, DEFAULT_USER)): cv.string,
93 | vol.Required(CONF_PASSWORD, default=data.get(CONF_PASSWORD)): cv.string,
94 | vol.Required(CONF_SCAN_INTERVAL, default=data.get(CONF_SCAN_INTERVAL)): int,
95 | vol.Required(CONF_VERIFY_SSL, default=data.get(CONF_VERIFY_SSL)): cv.boolean,
96 | })
97 |
98 | return self.async_show_form(step_id="init", data_schema=data_schema, errors=errors)
99 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/coordinator.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | import hashlib
3 | from datetime import timedelta, datetime
4 | from logging import Logger
5 | from collections.abc import Callable
6 | from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
7 | from tplinkrouterc6u import (
8 | TplinkRouterProvider,
9 | AbstractRouter,
10 | Firmware,
11 | Status,
12 | Connection,
13 | LTEStatus,
14 | SMS,
15 | )
16 | from homeassistant.core import HomeAssistant
17 | from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
18 | from .const import (
19 | DOMAIN,
20 | DEFAULT_NAME,
21 | )
22 |
23 |
24 | class TPLinkRouterCoordinator(DataUpdateCoordinator):
25 | def __init__(
26 | self,
27 | hass: HomeAssistant,
28 | router: AbstractRouter,
29 | update_interval: int,
30 | firmware: Firmware,
31 | status: Status,
32 | lte_status: LTEStatus | None,
33 | logger: Logger,
34 | unique_id: str
35 | ) -> None:
36 | self.router = router
37 | self.unique_id = unique_id
38 | self.status = status
39 | self.tracked = {}
40 | self.lte_status = lte_status
41 | self.device_info = DeviceInfo(
42 | configuration_url=router.host,
43 | connections={(CONNECTION_NETWORK_MAC, self.status.lan_macaddr)},
44 | identifiers={(DOMAIN, self.status.lan_macaddr)},
45 | manufacturer="TPLink",
46 | model=firmware.model,
47 | name=DEFAULT_NAME,
48 | sw_version=firmware.firmware_version,
49 | hw_version=firmware.hardware_version,
50 | )
51 |
52 | self.scan_stopped_at: datetime | None = None
53 | self._last_update_time: datetime | None = None
54 | self._sms_hashes: set[str] = set()
55 | self.new_sms: list[SMS] = []
56 |
57 | super().__init__(
58 | hass,
59 | logger,
60 | name=DOMAIN,
61 | update_interval=timedelta(seconds=update_interval),
62 | )
63 |
64 | @staticmethod
65 | async def get_client(hass: HomeAssistant, host: str, password: str, username: str, logger: Logger,
66 | verify_ssl: bool) -> AbstractRouter:
67 | return await hass.async_add_executor_job(TplinkRouterProvider.get_client, host, password, username,
68 | logger, verify_ssl)
69 |
70 | @staticmethod
71 | def request(router: AbstractRouter, callback: Callable):
72 | router.authorize()
73 | data = callback()
74 | router.logout()
75 |
76 | return data
77 |
78 | async def reboot(self) -> None:
79 | await self.hass.async_add_executor_job(TPLinkRouterCoordinator.request, self.router, self.router.reboot)
80 |
81 | async def set_wifi(self, wifi: Connection, enable: bool) -> None:
82 | def callback():
83 | self.router.set_wifi(wifi, enable)
84 |
85 | await self.hass.async_add_executor_job(TPLinkRouterCoordinator.request, self.router, callback)
86 |
87 | async def _async_update_data(self):
88 | """Asynchronous update of all data."""
89 | if self.scan_stopped_at is not None and self.scan_stopped_at > (datetime.now() - timedelta(minutes=20)):
90 | return
91 | self.scan_stopped_at = None
92 | self.status = await self.hass.async_add_executor_job(TPLinkRouterCoordinator.request, self.router,
93 | self.router.get_status)
94 | # Only fetch if router is lte_status compatible
95 | if self.lte_status is not None:
96 | self.lte_status = await self.hass.async_add_executor_job(
97 | TPLinkRouterCoordinator.request,
98 | self.router,
99 | self.router.get_lte_status,
100 | )
101 | await self._update_new_sms()
102 | self._last_update_time = datetime.now()
103 |
104 | async def _update_new_sms(self) -> None:
105 | if not hasattr(self.router, "get_sms") or self.lte_status is None:
106 | return
107 | sms_list = await self.hass.async_add_executor_job(TPLinkRouterCoordinator.request, self.router,
108 | self.router.get_sms)
109 | new_items = []
110 | for sms in sms_list:
111 | h = TPLinkRouterCoordinator._hash_item(sms)
112 | if self._last_update_time is None:
113 | self._sms_hashes.add(h)
114 | elif h not in self._sms_hashes:
115 | self._sms_hashes.add(h)
116 | new_items.append(sms)
117 |
118 | self.new_sms = new_items
119 |
120 | @staticmethod
121 | def _hash_item(sms: SMS) -> str:
122 | key = f"{sms.sender}|{sms.content}|{sms.received_at.isoformat()}"
123 | return hashlib.sha1(key.encode("utf-8")).hexdigest()
124 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/__init__.py:
--------------------------------------------------------------------------------
1 | from homeassistant.const import (
2 | CONF_HOST,
3 | CONF_PASSWORD,
4 | CONF_USERNAME,
5 | CONF_SCAN_INTERVAL,
6 | CONF_VERIFY_SSL,
7 | Platform,
8 | )
9 | from homeassistant.core import HomeAssistant, ServiceCall
10 | from homeassistant.config_entries import ConfigEntry
11 | from .const import DOMAIN, DEFAULT_USER, EVENT_NEW_SMS
12 | import logging
13 | from .coordinator import TPLinkRouterCoordinator
14 | from homeassistant.helpers import device_registry
15 |
16 | PLATFORMS: list[Platform] = [
17 | Platform.DEVICE_TRACKER,
18 | Platform.SENSOR,
19 | Platform.SWITCH,
20 | Platform.BUTTON,
21 | ]
22 |
23 | _LOGGER = logging.getLogger(__name__)
24 |
25 |
26 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
27 | # Construct the device
28 | host = entry.data[CONF_HOST]
29 | if not (host.startswith('http://') or host.startswith('https://')):
30 | host = "http://{}".format(host)
31 | verify_ssl = entry.data[CONF_VERIFY_SSL] if CONF_VERIFY_SSL in entry.data else True
32 | client = await TPLinkRouterCoordinator.get_client(
33 | hass=hass,
34 | host=host,
35 | password=entry.data[CONF_PASSWORD],
36 | username=entry.data.get(CONF_USERNAME, DEFAULT_USER),
37 | logger=_LOGGER,
38 | verify_ssl=verify_ssl
39 | )
40 |
41 | def callback():
42 | firm = client.get_firmware()
43 | stat = client.get_status()
44 | # Check if router is lte_status compatible
45 | lte_stat = None
46 | if hasattr(client, "get_lte_status"):
47 | try:
48 | lte_stat = client.get_lte_status()
49 | except Exception:
50 | pass
51 |
52 | return firm, stat, lte_stat
53 |
54 | firmware, status, lte_status = await hass.async_add_executor_job(TPLinkRouterCoordinator.request, client, callback)
55 |
56 | # Create device coordinator and fetch data
57 | coordinator = TPLinkRouterCoordinator(hass, client, entry.data[CONF_SCAN_INTERVAL], firmware, status,
58 | lte_status, _LOGGER, entry.entry_id)
59 |
60 | await coordinator.async_config_entry_first_refresh()
61 | _async_add_listeners(hass, coordinator)
62 | hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
63 |
64 | await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
65 | entry.async_on_unload(entry.add_update_listener(async_reload_entry))
66 |
67 | register_services(hass, coordinator)
68 |
69 | return True
70 |
71 |
72 | async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
73 | unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
74 |
75 | if unload_ok:
76 | hass.data[DOMAIN].pop(entry.entry_id)
77 | return unload_ok
78 |
79 |
80 | async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
81 | await hass.config_entries.async_reload(config_entry.entry_id)
82 |
83 |
84 | def register_services(hass: HomeAssistant, coord: TPLinkRouterCoordinator) -> None:
85 |
86 | if not hasattr(coord.router, "send_sms") or coord.lte_status is None:
87 | return
88 |
89 | dr = device_registry.async_get(hass)
90 |
91 | async def send_sms_service(service: ServiceCall) -> None:
92 | device = dr.async_get(service.data.get("device"))
93 | if device is None:
94 | _LOGGER.error('TplinkRouter Integration Exception - device was not found')
95 | return
96 | coordinator = None
97 | for key in device.config_entries:
98 | entry = hass.config_entries.async_get_entry(key)
99 | if not entry:
100 | continue
101 | if entry.domain != DOMAIN or not hasattr(hass.data[DOMAIN][key].router, "send_sms"):
102 | continue
103 | coordinator = hass.data[DOMAIN][key]
104 |
105 | if coordinator is None:
106 | _LOGGER.error('TplinkRouter Integration Exception - This device cannot send SMS')
107 | return
108 |
109 | def callback():
110 | coord.router.send_sms(service.data.get("number"), service.data.get("text"))
111 | await hass.async_add_executor_job(TPLinkRouterCoordinator.request, coord.router, callback)
112 |
113 | if not hass.services.has_service(DOMAIN, 'send_sms'):
114 | hass.services.async_register(DOMAIN, 'send_sms', send_sms_service)
115 |
116 |
117 | def _async_add_listeners(hass: HomeAssistant, coord: TPLinkRouterCoordinator) -> None:
118 |
119 | if not hasattr(coord.router, "get_sms") or coord.lte_status is None:
120 | return
121 |
122 | coord.async_add_listener(
123 | lambda: _fire_sms_event(hass, coord)
124 | )
125 |
126 |
127 | def _fire_sms_event(hass: HomeAssistant, coord: TPLinkRouterCoordinator) -> None:
128 | for sms in coord.new_sms:
129 | hass.bus.fire(
130 | EVENT_NEW_SMS,
131 | {
132 | 'sender': sms.sender,
133 | 'content': sms.content,
134 | 'received_at': sms.received_at.isoformat(),
135 | },
136 | )
137 | coord.new_sms = []
138 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/device_tracker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TypeAlias
4 | from homeassistant.components.device_tracker.config_entry import ScannerEntity
5 | from homeassistant.components.device_tracker.const import SourceType
6 | from homeassistant.config_entries import ConfigEntry
7 | from homeassistant.core import HomeAssistant, callback
8 | from homeassistant.helpers.entity_platform import AddEntitiesCallback
9 | from homeassistant.helpers.update_coordinator import CoordinatorEntity
10 | from .coordinator import TPLinkRouterCoordinator
11 | from .const import (
12 | DOMAIN,
13 | EVENT_NEW_DEVICE,
14 | EVENT_ONLINE,
15 | EVENT_OFFLINE,
16 | )
17 | from tplinkrouterc6u import Device
18 |
19 | MAC_ADDR: TypeAlias = str
20 |
21 |
22 | async def async_setup_entry(
23 | hass: HomeAssistant,
24 | entry: ConfigEntry,
25 | async_add_entities: AddEntitiesCallback,
26 | ) -> None:
27 | coordinator = hass.data[DOMAIN][entry.entry_id]
28 | tracked: dict[MAC_ADDR, TPLinkTracker] = {}
29 |
30 | @callback
31 | def coordinator_updated():
32 | """Update the status of the device."""
33 | update_items(coordinator, async_add_entities, tracked)
34 |
35 | entry.async_on_unload(coordinator.async_add_listener(coordinator_updated))
36 | coordinator_updated()
37 |
38 |
39 | @callback
40 | def update_items(
41 | coordinator: TPLinkRouterCoordinator,
42 | async_add_entities: AddEntitiesCallback,
43 | tracked: dict[MAC_ADDR, TPLinkTracker],
44 | ) -> None:
45 | """Update tracked device state from the hub."""
46 | new_tracked: list[TPLinkTracker] = []
47 | active: list[MAC_ADDR] = []
48 | fire_event = tracked != {}
49 | for device in coordinator.status.devices:
50 | active.append(device.macaddr)
51 | if device.macaddr not in tracked:
52 | tracked[device.macaddr] = TPLinkTracker(coordinator, device)
53 | new_tracked.append(tracked[device.macaddr])
54 | if fire_event:
55 | coordinator.hass.bus.fire(EVENT_NEW_DEVICE, tracked[device.macaddr].data)
56 | else:
57 | tracked[device.macaddr].device = device
58 | if fire_event and not tracked[device.macaddr].active and device.active:
59 | coordinator.hass.bus.fire(EVENT_ONLINE, tracked[device.macaddr].data)
60 | if fire_event and tracked[device.macaddr].active and not device.active:
61 | coordinator.hass.bus.fire(EVENT_OFFLINE, tracked[device.macaddr].data)
62 | tracked[device.macaddr].active = device.active
63 |
64 | if new_tracked:
65 | async_add_entities(new_tracked)
66 |
67 | for mac in tracked:
68 | if mac not in active and tracked[mac].active:
69 | tracked[mac].active = False
70 | coordinator.hass.bus.fire(EVENT_OFFLINE, tracked[mac].data)
71 |
72 |
73 | class TPLinkTracker(CoordinatorEntity, ScannerEntity):
74 | """Representation of network device."""
75 |
76 | def __init__(
77 | self,
78 | coordinator: TPLinkRouterCoordinator,
79 | data: Device,
80 | ) -> None:
81 | """Initialize the tracked device."""
82 | self.device = data
83 | self.active = True
84 |
85 | super().__init__(coordinator)
86 |
87 | @property
88 | def is_connected(self) -> bool:
89 | """Return true if the client is connected to the network."""
90 | return self.active
91 |
92 | @property
93 | def source_type(self) -> str:
94 | """Return the source type of the client."""
95 | return SourceType.ROUTER
96 |
97 | @property
98 | def name(self) -> str:
99 | """Return the name of the client."""
100 | return self.device.hostname if self.device.hostname != '' else self.device.macaddr
101 |
102 | @property
103 | def hostname(self) -> str:
104 | """Return the hostname of the client."""
105 | return self.device.hostname
106 |
107 | @property
108 | def mac_address(self) -> MAC_ADDR:
109 | """Return the mac address of the client."""
110 | return self.device.macaddr
111 |
112 | @property
113 | def ip_address(self) -> str:
114 | """Return the ip address of the client."""
115 | return self.device.ipaddr
116 |
117 | @property
118 | def unique_id(self) -> str:
119 | """Return an unique identifier for this device."""
120 | return f"{self.coordinator.unique_id}_{DOMAIN}_{self.mac_address}"
121 |
122 | @property
123 | def icon(self) -> str:
124 | """Return device icon."""
125 | return "mdi:lan-connect" if self.is_connected else "mdi:lan-disconnect"
126 |
127 | @property
128 | def extra_state_attributes(self) -> dict[str, str]:
129 | attributes = {
130 | 'connection': self.device.type.get_type(),
131 | 'band': self.device.type.get_band(),
132 | 'packets_sent': self.device.packets_sent,
133 | 'packets_received': self.device.packets_received
134 | }
135 | if self.device.down_speed is not None or self.device.up_speed is not None:
136 | attributes['up_speed'] = self.device.up_speed
137 | attributes['down_speed'] = self.device.down_speed
138 | return attributes
139 |
140 | @property
141 | def data(self) -> dict[str, str]:
142 | return dict(self.extra_state_attributes.items() | {
143 | 'hostname': self.hostname,
144 | 'ip_address': self.ip_address,
145 | 'mac_address': self.mac_address,
146 | }.items())
147 |
148 | @property
149 | def entity_registry_enabled_default(self) -> bool:
150 | return True
151 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/switch.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | from collections.abc import Callable
3 | from dataclasses import dataclass
4 | from datetime import datetime
5 | from typing import Any
6 | from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
7 | from homeassistant.config_entries import ConfigEntry
8 | from homeassistant.const import EntityCategory
9 | from homeassistant.core import HomeAssistant
10 | from homeassistant.helpers.entity_platform import AddEntitiesCallback
11 | from .const import DOMAIN
12 | from homeassistant.helpers.update_coordinator import CoordinatorEntity
13 | from .coordinator import TPLinkRouterCoordinator
14 | from tplinkrouterc6u import Connection
15 |
16 |
17 | @dataclass
18 | class TPLinkRouterSwitchEntityDescriptionMixin:
19 | method: Callable[[TPLinkRouterCoordinator, bool], Any]
20 | property: str
21 |
22 |
23 | @dataclass
24 | class TPLinkRouterSwitchEntityDescription(SwitchEntityDescription, TPLinkRouterSwitchEntityDescriptionMixin):
25 | """A class that describes sensor entities."""
26 |
27 |
28 | SWITCH_TYPES = (
29 | TPLinkRouterSwitchEntityDescription(
30 | key="wifi_guest_24g",
31 | name="Guest WIFI 2.4G",
32 | icon="mdi:wifi",
33 | entity_category=EntityCategory.CONFIG,
34 | property='guest_2g_enable',
35 | method=lambda coordinator, value: coordinator.set_wifi(Connection.GUEST_2G, value),
36 | ),
37 | TPLinkRouterSwitchEntityDescription(
38 | key="wifi_guest_5g",
39 | name="Guest WIFI 5G",
40 | icon="mdi:wifi",
41 | entity_category=EntityCategory.CONFIG,
42 | property='guest_5g_enable',
43 | method=lambda coordinator, value: coordinator.set_wifi(Connection.GUEST_5G, value),
44 | ),
45 | TPLinkRouterSwitchEntityDescription(
46 | key="wifi_guest_6g",
47 | name="Guest WIFI 6G",
48 | icon="mdi:wifi",
49 | entity_category=EntityCategory.CONFIG,
50 | property='guest_6g_enable',
51 | method=lambda coordinator, value: coordinator.set_wifi(Connection.GUEST_6G, value),
52 | ),
53 | TPLinkRouterSwitchEntityDescription(
54 | key="wifi_24g",
55 | name="WIFI 2.4G",
56 | icon="mdi:wifi",
57 | entity_category=EntityCategory.CONFIG,
58 | property='wifi_2g_enable',
59 | method=lambda coordinator, value: coordinator.set_wifi(Connection.HOST_2G, value),
60 | ),
61 | TPLinkRouterSwitchEntityDescription(
62 | key="wifi_5g",
63 | name="WIFI 5G",
64 | icon="mdi:wifi",
65 | entity_category=EntityCategory.CONFIG,
66 | property='wifi_5g_enable',
67 | method=lambda coordinator, value: coordinator.set_wifi(Connection.HOST_5G, value),
68 | ),
69 | TPLinkRouterSwitchEntityDescription(
70 | key="wifi_6g",
71 | name="WIFI 6G",
72 | icon="mdi:wifi",
73 | entity_category=EntityCategory.CONFIG,
74 | property='wifi_6g_enable',
75 | method=lambda coordinator, value: coordinator.set_wifi(Connection.HOST_6G, value),
76 | ),
77 | TPLinkRouterSwitchEntityDescription(
78 | key="iot_24g",
79 | name="IoT WIFI 2.4G",
80 | icon="mdi:wifi",
81 | entity_category=EntityCategory.CONFIG,
82 | property='iot_2g_enable',
83 | method=lambda coordinator, value: coordinator.set_wifi(Connection.IOT_2G, value),
84 | ),
85 | TPLinkRouterSwitchEntityDescription(
86 | key="iot_5g",
87 | name="IoT WIFI 5G",
88 | icon="mdi:wifi",
89 | entity_category=EntityCategory.CONFIG,
90 | property='iot_5g_enable',
91 | method=lambda coordinator, value: coordinator.set_wifi(Connection.IOT_5G, value),
92 | ),
93 | TPLinkRouterSwitchEntityDescription(
94 | key="iot_6g",
95 | name="IoT WIFI 6G",
96 | icon="mdi:wifi",
97 | entity_category=EntityCategory.CONFIG,
98 | property='iot_6g_enable',
99 | method=lambda coordinator, value: coordinator.set_wifi(Connection.IOT_6G, value),
100 | ),
101 | )
102 |
103 |
104 | async def async_setup_entry(
105 | hass: HomeAssistant,
106 | entry: ConfigEntry,
107 | async_add_entities: AddEntitiesCallback,
108 | ) -> None:
109 | coordinator = hass.data[DOMAIN][entry.entry_id]
110 |
111 | switches = []
112 |
113 | for description in SWITCH_TYPES:
114 | switches.append(TPLinkRouterSwitchEntity(coordinator, description))
115 |
116 | switches.append(TPLinkRouterScanEntity(coordinator))
117 |
118 | async_add_entities(switches, False)
119 |
120 |
121 | class TPLinkRouterSwitchEntity(
122 | CoordinatorEntity[TPLinkRouterCoordinator], SwitchEntity
123 | ):
124 | entity_description: TPLinkRouterSwitchEntityDescription
125 |
126 | def __init__(
127 | self,
128 | coordinator: TPLinkRouterCoordinator,
129 | description: TPLinkRouterSwitchEntityDescription,
130 | ) -> None:
131 | super().__init__(coordinator)
132 |
133 | self._attr_device_info = coordinator.device_info
134 | self._attr_unique_id = f"{coordinator.unique_id}_{DOMAIN}_{description.key}"
135 | self.entity_description = description
136 |
137 | @property
138 | def is_on(self) -> bool:
139 | """Return true if switch is on."""
140 | return getattr(self.coordinator.status, self.entity_description.property)
141 |
142 | @property
143 | def available(self) -> bool:
144 | """Return True if entity is available."""
145 | return getattr(self.coordinator.status, self.entity_description.property) is not None
146 |
147 | async def async_turn_on(self, **kwargs: Any) -> None:
148 | """Turn the entity on."""
149 | await self.entity_description.method(self.coordinator, True)
150 | setattr(self.coordinator.status, self.entity_description.property, True)
151 | self.async_write_ha_state()
152 |
153 | async def async_turn_off(self, **kwargs: Any) -> None:
154 | """Turn the entity off."""
155 | await self.entity_description.method(self.coordinator, False)
156 | setattr(self.coordinator.status, self.entity_description.property, False)
157 | self.async_write_ha_state()
158 |
159 |
160 | class TPLinkRouterScanEntity(
161 | CoordinatorEntity[TPLinkRouterCoordinator], SwitchEntity
162 | ):
163 | entity_description: SwitchEntityDescription
164 |
165 | def __init__(self, coordinator: TPLinkRouterCoordinator) -> None:
166 | super().__init__(coordinator)
167 |
168 | self._attr_device_info = coordinator.device_info
169 | self.entity_description = SwitchEntityDescription(
170 | key="scanning",
171 | name="Router data fetching",
172 | icon="mdi:connection",
173 | entity_category=EntityCategory.CONFIG,
174 | )
175 | self._attr_unique_id = f"{coordinator.unique_id}_{DOMAIN}_{self.entity_description.key}"
176 |
177 | @property
178 | def is_on(self) -> bool:
179 | """Return true if switch is on."""
180 | return self.coordinator.scan_stopped_at is None
181 |
182 | async def async_turn_on(self, **kwargs: Any) -> None:
183 | """Turn the entity on."""
184 | self.coordinator.scan_stopped_at = None
185 | self.async_write_ha_state()
186 |
187 | async def async_turn_off(self, **kwargs: Any) -> None:
188 | """Turn the entity off."""
189 | self.coordinator.scan_stopped_at = datetime.now()
190 | self.async_write_ha_state()
191 |
--------------------------------------------------------------------------------
/custom_components/tplink_router/sensor.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 | from collections.abc import Callable
3 | from typing import Any
4 | from homeassistant.components.sensor import (
5 | SensorStateClass,
6 | SensorEntity,
7 | SensorEntityDescription,
8 | )
9 | from homeassistant.const import PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfDataRate, UnitOfInformation
10 | from homeassistant.config_entries import ConfigEntry
11 | from homeassistant.core import HomeAssistant, callback
12 | from .const import DOMAIN
13 | from homeassistant.helpers.entity_platform import AddEntitiesCallback
14 | from homeassistant.helpers.update_coordinator import CoordinatorEntity
15 | from .coordinator import TPLinkRouterCoordinator
16 | from tplinkrouterc6u import Status, LTEStatus
17 |
18 |
19 | @dataclass
20 | class TPLinkRouterSensorRequiredKeysMixin:
21 | value: Callable[[Status], Any]
22 |
23 |
24 | @dataclass
25 | class TPLinkRouterLTESensorRequiredKeysMixin:
26 | value: Callable[[LTEStatus], Any]
27 |
28 |
29 | @dataclass
30 | class TPLinkRouterSensorEntityDescription(
31 | SensorEntityDescription, TPLinkRouterSensorRequiredKeysMixin
32 | ):
33 | """A class that describes sensor entities."""
34 |
35 | sensor_type: str = "status"
36 |
37 |
38 | @dataclass
39 | class TPLinkRouterLTESensorEntityDescription(
40 | SensorEntityDescription, TPLinkRouterLTESensorRequiredKeysMixin
41 | ):
42 | """A class that describes LTEStatus entities."""
43 |
44 | sensor_type: str = "lte_status"
45 |
46 |
47 | SENSOR_TYPES: tuple[TPLinkRouterSensorEntityDescription, ...] = (
48 | TPLinkRouterSensorEntityDescription(
49 | key="guest_wifi_clients_total",
50 | name="Total guest wifi clients",
51 | icon="mdi:account-multiple",
52 | state_class=SensorStateClass.TOTAL,
53 | value=lambda status: status.guest_clients_total,
54 | ),
55 | TPLinkRouterSensorEntityDescription(
56 | key="wifi_clients_total",
57 | name="Total main wifi clients",
58 | icon="mdi:account-multiple",
59 | state_class=SensorStateClass.TOTAL,
60 | value=lambda status: status.wifi_clients_total,
61 | ),
62 | TPLinkRouterSensorEntityDescription(
63 | key="wired_clients_total",
64 | name="Total wired clients",
65 | icon="mdi:account-multiple",
66 | state_class=SensorStateClass.TOTAL,
67 | value=lambda status: status.wired_total,
68 | ),
69 | TPLinkRouterSensorEntityDescription(
70 | key="iot_clients_total",
71 | name="Total IoT clients",
72 | icon="mdi:account-multiple",
73 | state_class=SensorStateClass.TOTAL,
74 | value=lambda status: status.iot_clients_total,
75 | ),
76 | TPLinkRouterSensorEntityDescription(
77 | key="clients_total",
78 | name="Total clients",
79 | icon="mdi:account-multiple",
80 | state_class=SensorStateClass.TOTAL,
81 | value=lambda status: status.clients_total,
82 | ),
83 | TPLinkRouterSensorEntityDescription(
84 | key="cpu_used",
85 | name="CPU used",
86 | icon="mdi:cpu-64-bit",
87 | state_class=SensorStateClass.MEASUREMENT,
88 | native_unit_of_measurement=PERCENTAGE,
89 | suggested_display_precision=1,
90 | value=lambda status: (status.cpu_usage * 100) if status.cpu_usage is not None else None,
91 | ),
92 | TPLinkRouterSensorEntityDescription(
93 | key="memory_used",
94 | name="Memory used",
95 | icon="mdi:memory",
96 | state_class=SensorStateClass.MEASUREMENT,
97 | native_unit_of_measurement=PERCENTAGE,
98 | suggested_display_precision=1,
99 | value=lambda status: (status.mem_usage * 100) if status.mem_usage is not None else None,
100 | ),
101 | TPLinkRouterSensorEntityDescription(
102 | key="conn_type",
103 | name="Connection Type",
104 | icon="mdi:wan",
105 | value=lambda status: status.conn_type,
106 | ),
107 | )
108 |
109 | LTE_SENSOR_TYPES: tuple[TPLinkRouterLTESensorEntityDescription, ...] = (
110 | TPLinkRouterLTESensorEntityDescription(
111 | key="lte_enabled",
112 | name="LTE Enabled",
113 | icon="mdi:sim-outline",
114 | value=lambda status: status.enable,
115 | ),
116 | TPLinkRouterLTESensorEntityDescription(
117 | key="lte_connect_status",
118 | name="LTE Connection Status",
119 | icon="mdi:sim-outline",
120 | value=lambda status: status.connect_status,
121 | ),
122 | TPLinkRouterLTESensorEntityDescription(
123 | key="lte_network_type",
124 | name="LTE Network Type",
125 | icon="mdi:sim-outline",
126 | value=lambda status: status.network_type,
127 | ),
128 | TPLinkRouterLTESensorEntityDescription(
129 | key="lte_network_type_info",
130 | name="LTE Network Type Info",
131 | icon="mdi:sim-outline",
132 | value=lambda status: status.network_type_info,
133 | ),
134 | TPLinkRouterLTESensorEntityDescription(
135 | key="lte_sim_status",
136 | name="LTE SIM Status",
137 | icon="mdi:sim-outline",
138 | value=lambda status: status.sim_status,
139 | ),
140 | TPLinkRouterLTESensorEntityDescription(
141 | key="lte_sim_status_info",
142 | name="LTE SIM Status Info",
143 | icon="mdi:sim-outline",
144 | value=lambda status: status.sim_status_info,
145 | ),
146 | TPLinkRouterLTESensorEntityDescription(
147 | key="lte_total_statistics",
148 | name="LTE Total Statistics",
149 | icon="mdi:sim-outline",
150 | state_class=SensorStateClass.TOTAL,
151 | native_unit_of_measurement=UnitOfInformation.BYTES,
152 | value=lambda status: status.total_statistics,
153 | ),
154 | TPLinkRouterLTESensorEntityDescription(
155 | key="lte_cur_rx_speed",
156 | name="LTE Current RX Speed",
157 | icon="mdi:sim-outline",
158 | state_class=SensorStateClass.MEASUREMENT,
159 | native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
160 | value=lambda status: status.cur_rx_speed,
161 | ),
162 | TPLinkRouterLTESensorEntityDescription(
163 | key="lte_cur_tx_speed",
164 | name="LTE Current TX Speed",
165 | icon="mdi:sim-outline",
166 | state_class=SensorStateClass.MEASUREMENT,
167 | native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
168 | value=lambda status: status.cur_tx_speed,
169 | ),
170 | TPLinkRouterLTESensorEntityDescription(
171 | key="lte_sms_unread_count",
172 | name="Unread SMS",
173 | icon="mdi:sim-outline",
174 | state_class=SensorStateClass.TOTAL,
175 | value=lambda status: status.sms_unread_count,
176 | ),
177 | TPLinkRouterLTESensorEntityDescription(
178 | key="lte_sig_level",
179 | name="LTE Signal Level",
180 | icon="mdi:sim-outline",
181 | state_class=SensorStateClass.MEASUREMENT,
182 | native_unit_of_measurement=PERCENTAGE,
183 | value=lambda status: status.sig_level * 25,
184 | ),
185 | TPLinkRouterLTESensorEntityDescription(
186 | key="lte_rsrp",
187 | name="LTE RSRP",
188 | icon="mdi:sim-outline",
189 | state_class=SensorStateClass.MEASUREMENT,
190 | native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
191 | value=lambda status: status.rsrp,
192 | ),
193 | TPLinkRouterLTESensorEntityDescription(
194 | key="lte_rsrq",
195 | name="LTE RSRQ",
196 | icon="mdi:sim-outline",
197 | state_class=SensorStateClass.MEASUREMENT,
198 | native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
199 | value=lambda status: status.rsrq,
200 | ),
201 | TPLinkRouterLTESensorEntityDescription(
202 | key="lte_snr",
203 | name="LTE SNR",
204 | icon="mdi:sim-outline",
205 | state_class=SensorStateClass.MEASUREMENT,
206 | native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
207 | value=lambda status: 0.1 * status.snr,
208 | ),
209 | TPLinkRouterLTESensorEntityDescription(
210 | key="lte_isp_name",
211 | name="LTE ISP Name",
212 | icon="mdi:sim-outline",
213 | value=lambda status: status.isp_name,
214 | ),
215 | )
216 |
217 |
218 | async def async_setup_entry(
219 | hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
220 | ) -> None:
221 | coordinator = hass.data[DOMAIN][entry.entry_id]
222 |
223 | sensors = []
224 |
225 | for description in SENSOR_TYPES:
226 | sensors.append(TPLinkRouterSensor(coordinator, description))
227 |
228 | if coordinator.lte_status is not None:
229 | for description in LTE_SENSOR_TYPES:
230 | sensors.append(TPLinkRouterSensor(coordinator, description))
231 |
232 | async_add_entities(sensors, False)
233 |
234 |
235 | class TPLinkRouterSensor(
236 | CoordinatorEntity[TPLinkRouterCoordinator], SensorEntity
237 | ):
238 | _attr_has_entity_name = True
239 | entity_description: TPLinkRouterSensorEntityDescription | TPLinkRouterLTESensorEntityDescription
240 |
241 | def __init__(
242 | self,
243 | coordinator: TPLinkRouterCoordinator,
244 | description: TPLinkRouterSensorEntityDescription | TPLinkRouterLTESensorEntityDescription,
245 | ) -> None:
246 | super().__init__(coordinator)
247 |
248 | self._attr_device_info = coordinator.device_info
249 | self._attr_unique_id = f"{coordinator.unique_id}_{DOMAIN}_{description.key}"
250 | self.entity_description = description
251 |
252 | @callback
253 | def _handle_coordinator_update(self) -> None:
254 | """Handle updated data from the coordinator."""
255 | coordinator_data = getattr(self.coordinator, self.entity_description.sensor_type)
256 | self._attr_native_value = self.entity_description.value(coordinator_data)
257 | self.async_write_ha_state()
258 |
259 | @property
260 | def available(self) -> bool:
261 | """Return True if entity is available."""
262 | coordinator_data = getattr(self.coordinator, self.entity_description.sensor_type)
263 | return self.entity_description.value(coordinator_data) is not None
264 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tp-Link router integration for Home Assistant (supports also Mercusys router)
2 | [](https://github.com/AlexandrErohin/home-assistant-tplink-router/releases/latest)
3 | [](https://github.com/hacs/integration)
4 | [](https://community.home-assistant.io/t/custom-component-tp-link-router-integration)
5 |
6 | Home Assistant component for TP-Link and Mercusys routers administration based on the [TP-Link Router API](https://github.com/AlexandrErohin/TP-Link-Archer-C6U)
7 |
8 | > [!WARNING]
9 | > A new router firmware update breaks the compatibility. Please try [this fix](https://github.com/AlexandrErohin/home-assistant-tplink-router/issues/220#issuecomment-3396658175)
10 |
11 | > [!WARNING]
12 | > Please temporarily disable the integration before accessing the router admin page. TP-Link admin page only allows one user at a time. This integration will log you out of the admin page every time it scans for updates (every 30s by default).
13 |
14 | See [Supported routers](#supports)
15 |
16 |
17 |
18 | ## Components
19 | ### Events
20 | - tplink_router_new_device: Fired when a new device appears in your network
21 | - tplink_router_device_offline: Fired when a device becomes offline
22 | - tplink_router_device_online: Fired when a device becomes online
23 | - tplink_router_new_sms: Fired when a new sms received by LTE router
24 | ### Switches
25 | - Router Reboot
26 | - Router data fetching - you may disable router data fetching before accessing the router, so it wont logging your out.
27 | If you forget to enable it back - it would be automatically enable after 20 minutes
28 | - 2.4Ghz host wifi Enable/Disable
29 | - 5Ghz host wifi Enable/Disable
30 | - 6Ghz host wifi Enable/Disable
31 | - 2.4Ghz guest wifi Enable/Disable
32 | - 5Ghz guest wifi Enable/Disable
33 | - 6Ghz guest wifi Enable/Disable
34 | - 2.4Ghz IoT wifi network Enable/Disable
35 | - 5Ghz IoT wifi network Enable/Disable
36 | - 6Ghz IoT wifi network Enable/Disable
37 |
38 | ### Sensors
39 | - Total amount of wired clients
40 | - Total amount of IoT clients
41 | - Total amount of host wifi clients
42 | - Total amount of guest wifi clients
43 | - Total amount of all connected clients
44 | - CPU used
45 | - Memory used
46 | - Connection Type
47 |
48 | For LTE Routers
49 | - LTE Enabled
50 | - LTE Connection Status
51 | - LTE Network Type
52 | - LTE SIM Status
53 | - LTE Total Statistics
54 | - LTE Current RX Speed
55 | - LTE Current TX Speed
56 | - Unread SMS
57 | - LTE Signal Level
58 | - LTE RSRP
59 | - LTE RSRQ
60 | - LTE SNR
61 | - LTE ISP Name
62 |
63 | ### Device Tracker
64 | - Track connected to router devices by MAC address with connection information
65 |
66 | To find your device - Go to `Developer tools` and search for your MAC address - you’ll find sensor like `device_tracker.YOUR_MAC` or `device_tracker.YOUR_PHONE_NAME`.
67 |
68 | It will also fire Home Assistant event when a device connects to router
69 |
70 | ### Services
71 | - Send SMS message - Available only for MR LTE routers
72 |
73 | ### Notification
74 | #### Device events
75 | To receive notifications of appearing a new device in your network, or becoming device online\offline add following lines to your `configuration.yaml` file:
76 | ```yaml
77 | automation:
78 | - alias: "New network device"
79 | trigger:
80 | platform: event
81 | event_type: tplink_router_new_device
82 | action:
83 | service: notify.mobile_app_
84 | data:
85 | content: >-
86 | New device appear {{ trigger.event.data.hostname }} with IP {{ trigger.event.data.ip_address }}
87 | ```
88 | Available events:
89 | - tplink_router_new_device: Fired when a new device appears in your network
90 | - tplink_router_device_offline: Fired when a device becomes offline
91 | - tplink_router_device_online: Fired when a device becomes online
92 |
93 | All available fields in `trigger.event.data`:
94 | - hostname
95 | - ip_address
96 | - mac_address
97 | - connection
98 | - band
99 | - packets_sent
100 | - packets_received
101 |
102 | #### SMS events only for MR LTE routers
103 | To receive notifications of receiving a new sms add following lines to your `configuration.yaml` file:
104 | ```yaml
105 | automation:
106 | - alias: "New sms"
107 | trigger:
108 | platform: event
109 | event_type: tplink_router_new_sms
110 | action:
111 | service: notify.mobile_app_
112 | data:
113 | content: >-
114 | A new SMS from {{ trigger.event.data.sender }} wth text: {{ trigger.event.data.content }}
115 | ```
116 | Available events:
117 | - tplink_router_new_sms: Fired when a new sms received by LTE router
118 |
119 | All available fields in `trigger.event.data`:
120 | - sender
121 | - content
122 | - received_at
123 |
124 | ### Send SMS only for MR LTE routers
125 | To send SMS add following lines to your automation in yaml:
126 | ```yaml
127 | ...
128 | action:
129 | - service: tplink_router.send_sms
130 | data:
131 | number: "+1234567890"
132 | text: "Hello World"
133 | device: pass_tplink_router_device_id_here
134 | ```
135 |
136 | Device id is required because user may have several routers that could send SMS - so you need to select the needed router.
137 | You can get the ID from the URL when you visit the tplink device page
138 |
139 | ## Installation
140 |
141 | ### HACS (recommended)
142 |
143 | Have [HACS](https://hacs.xyz/) installed, this will allow you to update easily.
144 |
145 |
146 |
147 | or go to Hacs and search for `TP-Link Router`.
148 |
149 | ### Manual
150 |
151 | 1. Locate the `custom_components` directory in your Home Assistant configuration directory. It may need to be created.
152 | 2. Copy the `custom_components/tplink_router` directory into the `custom_components` directory.
153 | 3. Restart Home Assistant.
154 |
155 | ## Configuration
156 | TP-Link Router is configured via the GUI. See [the HA docs](https://www.home-assistant.io/getting-started/integration/) for more details.
157 |
158 | The default data is preset already.
159 |
160 |
161 |
162 | 1. Go to the Settings->Devices & services.
163 | 2. Click on `+ ADD INTEGRATION`, search for `TP-Link Router`.
164 | 3. Fill Password.
165 | 4. Click `SUBMIT`
166 |
167 | NOTE!
168 | 1. If you use `https` connection to your router you may get error `certificate verify failed: EE certificate key too weak`. To fix this - unset `Verify ssl`
169 | 2. If you use `https` connection - You need to turn on "Local Management via HTTPS" (advanced->system->administration) in the router web UI
170 | 3. Use Local Password which is for Log In with Local Password. Login with TP-LINK ID doesnt work
171 |
172 |
173 |
174 | 4. If you got error - `check if the default router username is correct` The default username for most routers is `admin`. Some routers have the default username - `user`.
175 | 5. If you got error - `use web encrypted password instead` Read [web encrypted password](#encrypted_pass)
176 | 6. The TP-Link Web Interface only supports upto 1 user logged in at a time (for security reasons, apparently). So you will be logged out from router web interface when the integration updates data
177 |
178 | ### Web Encrypted Password
179 | If you got error - `use web encrypted password instead. Check the documentation!`
180 | 1. Go to the login page of your router. (default: 192.168.0.1).
181 | 2. Type in the password you use to login into the password field.
182 | 3. Click somewhere else on the page so that the password field is not selected anymore.
183 | 4. Open the JavaScript console of your browser (usually by pressing F12 and then clicking on "Console").
184 | 5. Type `document.getElementById("login-password").value;`
185 | 6. Copy the returned value as password and use it.
186 |
187 | ### Edit Configuration
188 | You may edit configuration data like:
189 | 1. Router url
190 | 2. Password
191 | 3. Scan interval
192 | 4. Verify https
193 |
194 | To do that:
195 |
196 | 1. Go to the Settings->Devices & services.
197 | 2. Search for `TP-Link Router`, and click on it.
198 | 3. Click on `CONFIGURE`
199 | 4. Edit the options you need and click `SUBMIT`
200 |
201 | ## Supported routers
202 | - [TP-LINK routers](#tplink)
203 | - [MERCUSYS routers](#mercusys)
204 | ### TP-LINK routers
205 | - Archer A6 (2.0, 4.0)
206 | - Archer A7 V5
207 | - Archer A8 (1.0, 2.20)
208 | - Archer A9 V6
209 | - Archer A20 v1.0
210 | - Archer AX10 v1.0
211 | - Archer AX12 v1.0
212 | - Archer AX17 v1.0
213 | - Archer AX20 (v1.0, v3.0)
214 | - Archer AX21 (v1.20, v3.0)
215 | - Archer AX23 (v1.0, v1.2)
216 | - Archer AX50 v1.0
217 | - Archer AX53 (v1.0, v2)
218 | - Archer AX55 (v1.0, V1.60, v4.0)
219 | - Archer AX58 v1.0
220 | - Archer AX72 V1
221 | - Archer AX73 (V1, V2.0)
222 | - Archer AX75 V1
223 | - Archer AX90 V1.20
224 | - Archer AX95 v1.0
225 | - Archer AXE75 V1
226 | - Archer AXE5400 v1.0
227 | - Archer AXE16000
228 | - Archer AX1800
229 | - Archer AX3000 V1
230 | - Archer AX6000 V1
231 | - Archer AX11000 V1
232 | - Archer BE220 v1.0
233 | - Archer BE230 v1.0
234 | - Archer BE400 v1.0
235 | - Archer BE550 v1.0
236 | - Archer BE800 v1.0
237 | - Archer BE805 v1.0
238 | - Archer BE3600 1.6
239 | - Archer C1200 (v1.0, v2.0)
240 | - Archer C2300 (v1.0, v2.0)
241 | - Archer C6 (v2.0, v3.0, v3.20, 4.0)
242 | - Archer C6U v1.0
243 | - Archer C7 (v4.0, v5.0)
244 | - Archer C24 (1.0, 2.0)
245 | - Archer C60 v2.0
246 | - Archer C64 1.0
247 | - Archer C80 (1.0, 2.20)
248 | - Archer C5400X V1
249 | - Archer GX90 v1.0
250 | - Archer MR200 (v2, v5, v5.3, v6.0)
251 | - Archer MR550 v1
252 | - Archer MR600 (v1, v2, v3)
253 | - Archer NX200 v2.0
254 | - Archer VR400 v3
255 | - Archer VR600 v3
256 | - Archer VR900v
257 | - Archer VR1200v v1
258 | - Archer VR2100v v1
259 | - Archer VX231v v1.0
260 | - Archer VX1800v v1.0
261 | - BE11000 2.0
262 | - Deco M4 2.0
263 | - Deco M4R 2.0
264 | - Deco M5 v3
265 | - Deco M9 Pro
266 | - Deco M9 Plus 1.0
267 | - Deco P7
268 | - Deco X20
269 | - Deco X50 v1.3
270 | - Deco X55 1.0
271 | - Deco X60 V3
272 | - Deco X90
273 | - Deco XE75 (v1.0, v2.0)
274 | - Deco XE75PRO (v3.0)
275 | - EX511 v2.0
276 | - HX510 v1.0
277 | - NX510v v1.0
278 | - TD-W9960 (v1, V1.20)
279 | - TL-MR100 v2.0
280 | - TL-MR105
281 | - TL-MR100-Outdoor v1.0
282 | - TL-MR110-Outdoor v1.0
283 | - TL-MR150 v2
284 | - TL-MR6400 (v5, v5.3)
285 | - TL-MR6500v
286 | - TL-WA1201 3.0
287 | - TL-WA3001 v1.0
288 | - TL-XDR3010 V2
289 | - TL-WDR3600 V1
290 | - TL-XDR6088 v1.0.30
291 | - VX420-G2h v1.1
292 | - VX800v v1
293 | - XC220-G3v v2.30
294 | - RE305 4.0
295 | - RE315 1.0
296 | - RE330 v1
297 | ### MERCUSYS routers
298 | - AC10 1.20
299 | - MR47BE v1.0
300 | - MR50G 1.0
301 | - H60XR 1.0
302 | - H47BE 2.0
303 | - Halo H80X 1.0
304 | - Halo H3000x 1.0
305 |
306 | Please let me know if you have tested integration with any other model. Open an issue with info about router's model, hardware and firmware versions.
307 |
308 | ## Adding Support For More Models
309 | Guidelines [CONTRIBUTING.md](https://github.com/AlexandrErohin/TP-Link-Archer-C6U/blob/master/CONTRIBUTING.md)
310 |
--------------------------------------------------------------------------------