Skip to content

Commit a9a6d06

Browse files
authored
Add backhaul diagnostic sensors and polling control (#501)
* Add backhaul diagnostic sensors and polling control * Update README.md * Update en.json * Update strings.json * Update strings.json Invalid string * Update strings.json * Update README.md * Update __init__.py * Update api.py * Update binary_sensor.py * Update sensor.py * Fix pre-commit issues
1 parent 68dc4ee commit a9a6d06

10 files changed

Lines changed: 209 additions & 26 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
1+
# TP-Link Deco (Community Fix)
2+
3+
⚠️ This fork restores and extends compatibility with newer Home Assistant versions.
4+
5+
The original repository is in maintenance mode and is no longer actively tested.
6+
This fork resolves compatibility issues and adds additional functionality.
7+
8+
---
9+
10+
## Added / Fixed in this fork
11+
12+
- ✅ Fix for Home Assistant 2026.4 compatibility
13+
- ✅ Diagnostic entities (including backhaul speed and max speed)
14+
- ✅ Polling control:
15+
16+
- pause/resume services
17+
18+
- switch entity to control polling from the UI
19+
- ✅ Improved stability when accessing the Deco web interface
20+
21+
👉 Use this fork if the original integration no longer works or if you need extended diagnostics and control.
22+
23+
---
24+
25+
## Services / Switch
26+
27+
Available services in Home Assistant:
28+
29+
- `tplink_deco.pause_polling`
30+
- `tplink_deco.resume_polling`
31+
32+
Available entity:
33+
34+
- `switch.deco_polling`
35+
36+
---
37+
38+
## Polling Control
39+
40+
Pause or resume polling to the Deco API.
41+
42+
### Benefits
43+
44+
- Reduces load on the Deco API by limiting continuous polling
45+
- Decreases network traffic and processing overhead
46+
- Improves debugging by allowing polling to be temporarily disabled
47+
- Helps prevent issues with limited concurrent sessions in the Deco web interface
48+
- Provides more control over integration behavior for advanced users
49+
50+
This is especially useful when accessing the Deco web interface, as the API allows only a limited number of concurrent sessions.
51+
152
# TP-Link Deco
253

354
[![GitHub Release][releases-shield]][releases]

custom_components/tplink_deco/__init__.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
from .const import DEVICE_TYPE_DECO
5050
from .const import DOMAIN
5151
from .const import PLATFORMS
52+
from .const import SERVICE_PAUSE_POLLING
5253
from .const import SERVICE_REBOOT_DECO
54+
from .const import SERVICE_RESUME_POLLING
5355
from .coordinator import TpLinkDeco
5456
from .coordinator import TpLinkDecoClient
5557
from .coordinator import TpLinkDecoData
@@ -161,6 +163,31 @@ async def async_create_config_data(hass: HomeAssistant, config_entry: ConfigEntr
161163
)
162164

163165

166+
async def async_pause_polling(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
167+
"""Pause Deco polling."""
168+
coordinator_decos = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR_DECOS_KEY]
169+
170+
if coordinator_decos.paused:
171+
_LOGGER.info("TP-Link Deco polling is already paused")
172+
return
173+
174+
coordinator_decos.paused = True
175+
_LOGGER.info("TP-Link Deco polling paused")
176+
177+
178+
async def async_resume_polling(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
179+
"""Resume Deco polling."""
180+
coordinator_decos = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR_DECOS_KEY]
181+
182+
if not coordinator_decos.paused:
183+
_LOGGER.info("TP-Link Deco polling is already running")
184+
return
185+
186+
coordinator_decos.paused = False
187+
_LOGGER.info("TP-Link Deco polling resumed")
188+
await coordinator_decos.async_request_refresh()
189+
190+
164191
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
165192
"""Set up this integration using UI."""
166193
_LOGGER.debug("async_setup_entry: Config entry %s", config_entry.entry_id)
@@ -204,6 +231,26 @@ async def async_reboot_deco(service: ServiceCall) -> None:
204231
),
205232
)
206233

234+
async def handle_pause_polling(service: ServiceCall) -> None:
235+
"""Handle pause polling service."""
236+
await async_pause_polling(hass, config_entry)
237+
238+
async def handle_resume_polling(service: ServiceCall) -> None:
239+
"""Handle resume polling service."""
240+
await async_resume_polling(hass, config_entry)
241+
242+
hass.services.async_register(
243+
DOMAIN,
244+
SERVICE_PAUSE_POLLING,
245+
handle_pause_polling,
246+
)
247+
248+
hass.services.async_register(
249+
DOMAIN,
250+
SERVICE_RESUME_POLLING,
251+
handle_resume_polling,
252+
)
253+
207254
config_entry.async_on_unload(config_entry.add_update_listener(update_listener))
208255

209256
return True
@@ -230,7 +277,8 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
230277
)
231278
if unloaded:
232279
hass.data[DOMAIN].pop(config_entry.entry_id)
233-
280+
hass.services.async_remove(DOMAIN, SERVICE_PAUSE_POLLING)
281+
hass.services.async_remove(DOMAIN, SERVICE_RESUME_POLLING)
234282
return unloaded
235283

236284

custom_components/tplink_deco/api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,11 @@ async def _async_post(
398398
data: Any,
399399
) -> dict:
400400
headers = {CONTENT_TYPE: "application/json"}
401+
# Gebruik een dictionary voor cookies in plaats van een string in headers
401402
request_cookies = {}
402403
if self._cookie is not None:
403404
try:
404-
# Split 'sysauth=abc' into {'sysauth': 'abc'}
405+
# Split 'sysauth=abc' naar {'sysauth': 'abc'}
405406
cookie_parts = self._cookie.split("=", 1)
406407
if len(cookie_parts) == 2:
407408
request_cookies[cookie_parts[0]] = cookie_parts[1]
@@ -414,7 +415,7 @@ async def _async_post(
414415
params=params,
415416
data=data,
416417
headers=headers,
417-
cookies=request_cookies,
418+
cookies=request_cookies, # Gebruik de cookies parameter
418419
ssl=self._ssl_context,
419420
)
420421
response.raise_for_status()
@@ -427,7 +428,7 @@ async def _async_post(
427428
_LOGGER.debug("Found new cookie: %s", self._cookie)
428429
break
429430

430-
# Sometimes server responses with incorrect content type, so disable the check
431+
# Soms antwoordt de server met de verkeerde content-type
431432
response_json = await response.json(content_type=None)
432433
if "error_code" in response_json:
433434
error_code = response_json.get("error_code")

custom_components/tplink_deco/binary_sensor.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def add_binary_sensors_for_deco(deco: TpLinkDeco) -> None:
3333
),
3434
TplinkDecoOnlineBinarySensor(
3535
coordinator_decos,
36-
deco.mac,
36+
deco.mac,
3737
),
3838
]
3939
)
@@ -84,14 +84,13 @@ def is_on(self) -> bool:
8484

8585
if isinstance(value, str):
8686
return value.lower() in ("online", "true", "1", "yes")
87-
87+
8888
return bool(value)
89-
89+
9090
@property
9191
def available(self) -> bool:
9292
return self._deco is not None and self._deco.internet_online is not None
9393

94-
9594
@property
9695
def device_info(self):
9796
"""Return device info."""
@@ -100,6 +99,7 @@ def device_info(self):
10099
self.coordinator.data.master_deco,
101100
)
102101

102+
103103
class TplinkDecoOnlineBinarySensor(CoordinatorEntity, BinarySensorEntity):
104104
"""TP-Link Deco online (mesh/backhaul) status."""
105105

@@ -129,4 +129,4 @@ def device_info(self):
129129
return create_device_info(
130130
self._deco,
131131
self.coordinator.data.master_deco,
132-
)
132+
)

custom_components/tplink_deco/const.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
"""Constants for TP-Link Deco."""
22

3-
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
4-
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
53
from homeassistant.components.device_tracker.const import (
64
DEFAULT_CONSIDER_HOME as DEFAULT_CONSIDER_HOME_SPAN,
75
)
8-
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
96

107
# Base component constants
118
DOMAIN = "tplink_deco"
@@ -53,6 +50,7 @@
5350

5451
# Services
5552
SERVICE_REBOOT_DECO = "reboot_deco"
56-
53+
SERVICE_PAUSE_POLLING = "pause_polling"
54+
SERVICE_RESUME_POLLING = "resume_polling"
5755
# Platforms
58-
PLATFORMS = [DEVICE_TRACKER_DOMAIN, SENSOR_DOMAIN, BINARY_SENSOR_DOMAIN]
56+
PLATFORMS = ["device_tracker", "sensor", "binary_sensor", "switch"]

custom_components/tplink_deco/coordinator.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def __init__(self, mac: str) -> None:
7272
self.bssid_band5 = None
7373
self.signal_band2_4 = None
7474
self.signal_band5 = None
75+
self.backhaul_speed = None
76+
self.backhaul_max_speed = None
7577

7678
def update(
7779
self,
@@ -92,14 +94,16 @@ def update(
9294
elif isinstance(inet, str):
9395
self.internet_online = inet.lower() in ("online", "connected", "up")
9496
else:
95-
self.internet_online = bool(inet)
97+
self.internet_online = bool(inet)
9698
self.master = data.get("role") == "master"
9799
self.connection_type = data.get("connection_type")
98100
self.bssid_band2_4 = data.get("bssid_2g")
99101
self.bssid_band5 = data.get("bssid_5g")
100102
signal_level = data.get("signal_level", {})
101103
self.signal_band2_4 = signal_level.get("band2_4")
102104
self.signal_band5 = signal_level.get("band5")
105+
self.backhaul_speed = data.get("backhual_speed")
106+
self.backhaul_max_speed = data.get("backhual_max_speed")
103107

104108

105109
class TpLinkDecoClient:
@@ -171,8 +175,14 @@ def __init__(
171175
# Must happen after super().__init__
172176
self.data = TpLinkDecoData() if data is None else data
173177

178+
self.paused = False
179+
174180
async def _async_update_data(self):
175181
"""Update data via api."""
182+
if self.paused:
183+
_LOGGER.debug("Deco polling is paused")
184+
return self.data
185+
176186
new_decos = await async_call_and_propagate_config_error(
177187
self.api.async_list_devices
178188
)
@@ -244,6 +254,10 @@ def __init__(
244254

245255
async def _async_update_data(self):
246256
"""Update data via api."""
257+
if self._deco_update_coordinator.paused:
258+
_LOGGER.debug("Deo client polling is paused")
259+
return self.data
260+
247261
if len(self._deco_update_coordinator.data.decos) == 0:
248262
return
249263

custom_components/tplink_deco/sensor.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from dataclasses import dataclass
44
import logging
5+
from typing import Any
56
from typing import Callable
67

78
from homeassistant.components.sensor import SensorEntity
@@ -33,7 +34,7 @@
3334
class TplinkDecoDiagnosticSensorDescription(SensorEntityDescription):
3435
"""Description of a TP-Link Deco diagnostic sensor."""
3536

36-
value_fn: Callable[[TpLinkDeco], str | None]
37+
value_fn: Callable[[TpLinkDeco], Any]
3738

3839

3940
DIAGNOSTIC_SENSOR_DESCRIPTIONS: tuple[TplinkDecoDiagnosticSensorDescription, ...] = (
@@ -69,6 +70,22 @@ class TplinkDecoDiagnosticSensorDescription(SensorEntityDescription):
6970
else deco.connection_type
7071
),
7172
),
73+
TplinkDecoDiagnosticSensorDescription(
74+
key="backhaul_speed",
75+
name="Backhaul speed",
76+
entity_category=EntityCategory.DIAGNOSTIC,
77+
entity_registry_enabled_default=False,
78+
native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
79+
value_fn=lambda deco: deco.backhaul_speed,
80+
),
81+
TplinkDecoDiagnosticSensorDescription(
82+
key="backhaul_max_speed",
83+
name="Backhaul max speed",
84+
entity_category=EntityCategory.DIAGNOSTIC,
85+
entity_registry_enabled_default=False,
86+
native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
87+
value_fn=lambda deco: deco.backhaul_max_speed,
88+
),
7289
)
7390

7491

@@ -179,7 +196,7 @@ def __init__(
179196
self._attr_state_class = SensorStateClass.MEASUREMENT
180197
self._attr_unique_id = unique_id
181198
super().__init__(coordinator_clients)
182-
self._update_state() # Must happen after init
199+
self._update_state()
183200

184201
@property
185202
def device_info(self) -> DeviceInfo:
@@ -224,7 +241,7 @@ def __init__(
224241
self._deco_mac = deco_mac
225242
self._attr_unique_id = f"{deco_mac}_client_count"
226243
super().__init__(coordinator_clients)
227-
self._update_state() # Must happen after init
244+
self._update_state()
228245

229246
@property
230247
def _deco(self) -> TpLinkDeco:
@@ -280,6 +297,8 @@ def device_info(self) -> DeviceInfo:
280297
return create_device_info(self._deco, self.coordinator.data.master_deco)
281298

282299
@property
283-
def native_value(self) -> str | None:
300+
def native_value(self):
284301
value = self.entity_description.value_fn(self._deco)
285-
return value if value else None
302+
if value is None or value == "" or value == []:
303+
return None
304+
return value

custom_components/tplink_deco/strings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"config": {
33
"step": {
44
"user": {
5-
"description": "Use the credentials for the admin web portal. Please follow the instructions at https://github.com/amosyuen/ha-tplink-deco#configuration-important-please-read",
5+
"description": "Use the credentials for the admin web portal. See documentation for more information",
66
"data": {
77
"host": "[%key:common::config_flow::data::host%]",
88
"username": "[%key:common::config_flow::data::username%]",
@@ -19,7 +19,7 @@
1919
}
2020
},
2121
"reauth_confirm": {
22-
"description": "Problem with login credentials. Maybe caused by logging in on a separate device. See https://github.com/amosyuen/ha-tplink-deco#login-credentials",
22+
"description": "Problem with login credentials. Maybe caused by logging in on a separate device. See documentation for more information",
2323
"data": {
2424
"username": "[%key:common::config_flow::data::username%]",
2525
"password": "[%key:common::config_flow::data::password%]"
@@ -36,7 +36,7 @@
3636
"options": {
3737
"step": {
3838
"init": {
39-
"description": "Use the credentials for the admin web portal. Please follow the instructions at https://github.com/amosyuen/ha-tplink-deco#configuration-important-please-read",
39+
"description": "Use the credentials for the admin web portal. See documentation for more information",
4040
"data": {
4141
"host": "[%key:common::config_flow::data::host%]",
4242
"username": "[%key:common::config_flow::data::username%]",

0 commit comments

Comments
 (0)