mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Fix tplink overloading power strips (#104208)
This commit is contained in:
parent
0ff5ccb7fd
commit
f5e7631e84
@ -29,6 +29,7 @@ from homeassistant.helpers.typing import ConfigType
|
|||||||
|
|
||||||
from .const import DOMAIN, PLATFORMS
|
from .const import DOMAIN, PLATFORMS
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
from .coordinator import TPLinkDataUpdateCoordinator
|
||||||
|
from .models import TPLinkData
|
||||||
|
|
||||||
DISCOVERY_INTERVAL = timedelta(minutes=15)
|
DISCOVERY_INTERVAL = timedelta(minutes=15)
|
||||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||||
@ -102,7 +103,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
f"Unexpected device found at {host}; expected {entry.unique_id}, found {found_mac}"
|
f"Unexpected device found at {host}; expected {entry.unique_id}, found {found_mac}"
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = TPLinkDataUpdateCoordinator(hass, device)
|
parent_coordinator = TPLinkDataUpdateCoordinator(hass, device, timedelta(seconds=5))
|
||||||
|
child_coordinators: list[TPLinkDataUpdateCoordinator] = []
|
||||||
|
|
||||||
|
if device.is_strip:
|
||||||
|
child_coordinators = [
|
||||||
|
# The child coordinators only update energy data so we can
|
||||||
|
# set a longer update interval to avoid flooding the device
|
||||||
|
TPLinkDataUpdateCoordinator(hass, child, timedelta(seconds=60))
|
||||||
|
for child in device.children
|
||||||
|
]
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = TPLinkData(
|
||||||
|
parent_coordinator, child_coordinators
|
||||||
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -111,7 +125,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
hass_data: dict[str, Any] = hass.data[DOMAIN]
|
hass_data: dict[str, Any] = hass.data[DOMAIN]
|
||||||
device: SmartDevice = hass_data[entry.entry_id].device
|
data: TPLinkData = hass_data[entry.entry_id]
|
||||||
|
device = data.parent_coordinator.device
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||||
hass_data.pop(entry.entry_id)
|
hass_data.pop(entry.entry_id)
|
||||||
await device.protocol.close()
|
await device.protocol.close()
|
||||||
|
@ -22,11 +22,10 @@ class TPLinkDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device: SmartDevice,
|
device: SmartDevice,
|
||||||
|
update_interval: timedelta,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize DataUpdateCoordinator to gather data for specific SmartPlug."""
|
"""Initialize DataUpdateCoordinator to gather data for specific SmartPlug."""
|
||||||
self.device = device
|
self.device = device
|
||||||
self.update_children = True
|
|
||||||
update_interval = timedelta(seconds=5)
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
@ -39,19 +38,9 @@ class TPLinkDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_request_refresh_without_children(self) -> None:
|
|
||||||
"""Request a refresh without the children."""
|
|
||||||
# If the children do get updated this is ok as this is an
|
|
||||||
# optimization to reduce the number of requests on the device
|
|
||||||
# when we do not need it.
|
|
||||||
self.update_children = False
|
|
||||||
await self.async_request_refresh()
|
|
||||||
|
|
||||||
async def _async_update_data(self) -> None:
|
async def _async_update_data(self) -> None:
|
||||||
"""Fetch all device and sensor data from api."""
|
"""Fetch all device and sensor data from api."""
|
||||||
try:
|
try:
|
||||||
await self.device.update(update_children=self.update_children)
|
await self.device.update(update_children=False)
|
||||||
except SmartDeviceException as ex:
|
except SmartDeviceException as ex:
|
||||||
raise UpdateFailed(ex) from ex
|
raise UpdateFailed(ex) from ex
|
||||||
finally:
|
|
||||||
self.update_children = True
|
|
||||||
|
@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
from .models import TPLinkData
|
||||||
|
|
||||||
TO_REDACT = {
|
TO_REDACT = {
|
||||||
# Entry fields
|
# Entry fields
|
||||||
@ -36,7 +36,8 @@ async def async_get_config_entry_diagnostics(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: ConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
data: TPLinkData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
coordinator = data.parent_coordinator
|
||||||
oui = format_mac(coordinator.device.mac)[:8].upper()
|
oui = format_mac(coordinator.device.mac)[:8].upper()
|
||||||
return async_redact_data(
|
return async_redact_data(
|
||||||
{"device_last_response": coordinator.device.internal_state, "oui": oui},
|
{"device_last_response": coordinator.device.internal_state, "oui": oui},
|
||||||
|
@ -24,7 +24,7 @@ def async_refresh_after(
|
|||||||
|
|
||||||
async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
await func(self, *args, **kwargs)
|
await func(self, *args, **kwargs)
|
||||||
await self.coordinator.async_request_refresh_without_children()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
return _async_wrap
|
return _async_wrap
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from . import legacy_device_id
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
from .coordinator import TPLinkDataUpdateCoordinator
|
||||||
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
||||||
|
from .models import TPLinkData
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -132,14 +133,12 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up switches."""
|
"""Set up switches."""
|
||||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
data: TPLinkData = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
if coordinator.device.is_light_strip:
|
parent_coordinator = data.parent_coordinator
|
||||||
|
device = parent_coordinator.device
|
||||||
|
if device.is_light_strip:
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[TPLinkSmartLightStrip(cast(SmartLightStrip, device), parent_coordinator)]
|
||||||
TPLinkSmartLightStrip(
|
|
||||||
cast(SmartLightStrip, coordinator.device), coordinator
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
@ -152,9 +151,9 @@ async def async_setup_entry(
|
|||||||
SEQUENCE_EFFECT_DICT,
|
SEQUENCE_EFFECT_DICT,
|
||||||
"async_set_sequence_effect",
|
"async_set_sequence_effect",
|
||||||
)
|
)
|
||||||
elif coordinator.device.is_bulb or coordinator.device.is_dimmer:
|
elif device.is_bulb or device.is_dimmer:
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[TPLinkSmartBulb(cast(SmartBulb, coordinator.device), coordinator)]
|
[TPLinkSmartBulb(cast(SmartBulb, device), parent_coordinator)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
14
homeassistant/components/tplink/models.py
Normal file
14
homeassistant/components/tplink/models.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
"""The tplink integration models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from .coordinator import TPLinkDataUpdateCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class TPLinkData:
|
||||||
|
"""Data for the tplink integration."""
|
||||||
|
|
||||||
|
parent_coordinator: TPLinkDataUpdateCoordinator
|
||||||
|
children_coordinators: list[TPLinkDataUpdateCoordinator]
|
@ -33,6 +33,7 @@ from .const import (
|
|||||||
)
|
)
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
from .coordinator import TPLinkDataUpdateCoordinator
|
||||||
from .entity import CoordinatedTPLinkEntity
|
from .entity import CoordinatedTPLinkEntity
|
||||||
|
from .models import TPLinkData
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -106,31 +107,39 @@ def async_emeter_from_device(
|
|||||||
return None if device.is_bulb else 0.0
|
return None if device.is_bulb else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def _async_sensors_for_device(
|
||||||
|
device: SmartDevice, coordinator: TPLinkDataUpdateCoordinator
|
||||||
|
) -> list[SmartPlugSensor]:
|
||||||
|
"""Generate the sensors for the device."""
|
||||||
|
return [
|
||||||
|
SmartPlugSensor(device, coordinator, description)
|
||||||
|
for description in ENERGY_SENSORS
|
||||||
|
if async_emeter_from_device(device, description) is not None
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up sensors."""
|
"""Set up sensors."""
|
||||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
data: TPLinkData = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
|
parent_coordinator = data.parent_coordinator
|
||||||
|
children_coordinators = data.children_coordinators
|
||||||
entities: list[SmartPlugSensor] = []
|
entities: list[SmartPlugSensor] = []
|
||||||
parent = coordinator.device
|
parent = parent_coordinator.device
|
||||||
if not parent.has_emeter:
|
if not parent.has_emeter:
|
||||||
return
|
return
|
||||||
|
|
||||||
def _async_sensors_for_device(device: SmartDevice) -> list[SmartPlugSensor]:
|
|
||||||
return [
|
|
||||||
SmartPlugSensor(device, coordinator, description)
|
|
||||||
for description in ENERGY_SENSORS
|
|
||||||
if async_emeter_from_device(device, description) is not None
|
|
||||||
]
|
|
||||||
|
|
||||||
if parent.is_strip:
|
if parent.is_strip:
|
||||||
# Historically we only add the children if the device is a strip
|
# Historically we only add the children if the device is a strip
|
||||||
for child in parent.children:
|
for idx, child in enumerate(parent.children):
|
||||||
entities.extend(_async_sensors_for_device(child))
|
entities.extend(
|
||||||
|
_async_sensors_for_device(child, children_coordinators[idx])
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
entities.extend(_async_sensors_for_device(parent))
|
entities.extend(_async_sensors_for_device(parent, parent_coordinator))
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from . import legacy_device_id
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
from .coordinator import TPLinkDataUpdateCoordinator
|
||||||
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
||||||
|
from .models import TPLinkData
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -26,8 +27,9 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up switches."""
|
"""Set up switches."""
|
||||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
data: TPLinkData = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
device = cast(SmartPlug, coordinator.device)
|
parent_coordinator = data.parent_coordinator
|
||||||
|
device = cast(SmartPlug, parent_coordinator.device)
|
||||||
if not device.is_plug and not device.is_strip and not device.is_dimmer:
|
if not device.is_plug and not device.is_strip and not device.is_dimmer:
|
||||||
return
|
return
|
||||||
entities: list = []
|
entities: list = []
|
||||||
@ -35,11 +37,11 @@ async def async_setup_entry(
|
|||||||
# Historically we only add the children if the device is a strip
|
# Historically we only add the children if the device is a strip
|
||||||
_LOGGER.debug("Initializing strip with %s sockets", len(device.children))
|
_LOGGER.debug("Initializing strip with %s sockets", len(device.children))
|
||||||
for child in device.children:
|
for child in device.children:
|
||||||
entities.append(SmartPlugSwitchChild(device, coordinator, child))
|
entities.append(SmartPlugSwitchChild(device, parent_coordinator, child))
|
||||||
elif device.is_plug:
|
elif device.is_plug:
|
||||||
entities.append(SmartPlugSwitch(device, coordinator))
|
entities.append(SmartPlugSwitch(device, parent_coordinator))
|
||||||
|
|
||||||
entities.append(SmartPlugLedSwitch(device, coordinator))
|
entities.append(SmartPlugLedSwitch(device, parent_coordinator))
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user