mirror of
https://github.com/home-assistant/core
synced 2024-07-15 09:42:11 +02: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 .coordinator import TPLinkDataUpdateCoordinator
|
||||
from .models import TPLinkData
|
||||
|
||||
DISCOVERY_INTERVAL = timedelta(minutes=15)
|
||||
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}"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
"""Unload a config entry."""
|
||||
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):
|
||||
hass_data.pop(entry.entry_id)
|
||||
await device.protocol.close()
|
||||
|
@ -22,11 +22,10 @@ class TPLinkDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
device: SmartDevice,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize DataUpdateCoordinator to gather data for specific SmartPlug."""
|
||||
self.device = device
|
||||
self.update_children = True
|
||||
update_interval = timedelta(seconds=5)
|
||||
super().__init__(
|
||||
hass,
|
||||
_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:
|
||||
"""Fetch all device and sensor data from api."""
|
||||
try:
|
||||
await self.device.update(update_children=self.update_children)
|
||||
await self.device.update(update_children=False)
|
||||
except SmartDeviceException as 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 .const import DOMAIN
|
||||
from .coordinator import TPLinkDataUpdateCoordinator
|
||||
from .models import TPLinkData
|
||||
|
||||
TO_REDACT = {
|
||||
# Entry fields
|
||||
@ -36,7 +36,8 @@ async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""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()
|
||||
return async_redact_data(
|
||||
{"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:
|
||||
await func(self, *args, **kwargs)
|
||||
await self.coordinator.async_request_refresh_without_children()
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
return _async_wrap
|
||||
|
||||
|
@ -28,6 +28,7 @@ from . import legacy_device_id
|
||||
from .const import DOMAIN
|
||||
from .coordinator import TPLinkDataUpdateCoordinator
|
||||
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
||||
from .models import TPLinkData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -132,14 +133,12 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switches."""
|
||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
if coordinator.device.is_light_strip:
|
||||
data: TPLinkData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
parent_coordinator = data.parent_coordinator
|
||||
device = parent_coordinator.device
|
||||
if device.is_light_strip:
|
||||
async_add_entities(
|
||||
[
|
||||
TPLinkSmartLightStrip(
|
||||
cast(SmartLightStrip, coordinator.device), coordinator
|
||||
)
|
||||
]
|
||||
[TPLinkSmartLightStrip(cast(SmartLightStrip, device), parent_coordinator)]
|
||||
)
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
@ -152,9 +151,9 @@ async def async_setup_entry(
|
||||
SEQUENCE_EFFECT_DICT,
|
||||
"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(
|
||||
[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 .entity import CoordinatedTPLinkEntity
|
||||
from .models import TPLinkData
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -106,31 +107,39 @@ def async_emeter_from_device(
|
||||
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(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""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] = []
|
||||
parent = coordinator.device
|
||||
parent = parent_coordinator.device
|
||||
if not parent.has_emeter:
|
||||
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:
|
||||
# Historically we only add the children if the device is a strip
|
||||
for child in parent.children:
|
||||
entities.extend(_async_sensors_for_device(child))
|
||||
for idx, child in enumerate(parent.children):
|
||||
entities.extend(
|
||||
_async_sensors_for_device(child, children_coordinators[idx])
|
||||
)
|
||||
else:
|
||||
entities.extend(_async_sensors_for_device(parent))
|
||||
entities.extend(_async_sensors_for_device(parent, parent_coordinator))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
@ -16,6 +16,7 @@ from . import legacy_device_id
|
||||
from .const import DOMAIN
|
||||
from .coordinator import TPLinkDataUpdateCoordinator
|
||||
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
||||
from .models import TPLinkData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -26,8 +27,9 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switches."""
|
||||
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
device = cast(SmartPlug, coordinator.device)
|
||||
data: TPLinkData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
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:
|
||||
return
|
||||
entities: list = []
|
||||
@ -35,11 +37,11 @@ async def async_setup_entry(
|
||||
# Historically we only add the children if the device is a strip
|
||||
_LOGGER.debug("Initializing strip with %s sockets", len(device.children))
|
||||
for child in device.children:
|
||||
entities.append(SmartPlugSwitchChild(device, coordinator, child))
|
||||
entities.append(SmartPlugSwitchChild(device, parent_coordinator, child))
|
||||
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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user