Prevent tplink missing devices and unavailable state (#39762)
* Adds self to codeowners for tplink * Adds retry to update to prevent missing devices * Runs through isort and corrects async commit * Runs through black * Runs through pre-checks * Corrects and matches var names * Re-runs through black * Corrects var name * Removes the retry loop and in favor of async add * Cleanup imports * Removes no longer valid test * Removes is_ready and only log retry once * Corrects switch logging vars * Adds list of entities to add_entities * Consumes exception for attempt_update * Consumes specific exception * Removes unnecessary update * Reducing back to 2 seconds * Removes useless return * Call get_sysinfo for all at once * Formated black * Adds missing docstirng * Corrects docstring * Update homeassistant/components/tplink/light.py Co-authored-by: Anders Melchiorsen <amelchio@nogoto.net> * Corrects sysinfo call * Adds default for host vars * Adds log when device responds again * Revert host alias default * Removes unncessary host var * Removes host var * Get device details from sysinfo * Use host and alias for log msg * Gets hosts from smartbulb * Changes retry logging to debug * Attempts coverage add * Removes unused import * Updates tests for new retry * Runs through isort * Removes unneeded try * Prevents static entries from failing integration * Format black * Forces an update after turn on off * Remove common test * Revert update after turn_on off * Adds patch for sleep_time 0 * Returns False when update fails Co-authored-by: Anders Melchiorsen <amelchio@nogoto.net>
This commit is contained in:
parent
366006e0aa
commit
874e1f6103
8 changed files with 213 additions and 263 deletions
|
@ -1,8 +1,6 @@
|
|||
"""Common code for tplink."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any, Callable, List
|
||||
from typing import List
|
||||
|
||||
from pyHS100 import (
|
||||
Discover,
|
||||
|
@ -113,89 +111,19 @@ def get_static_devices(config_data) -> SmartDevices:
|
|||
for type_ in [CONF_LIGHT, CONF_SWITCH, CONF_STRIP, CONF_DIMMER]:
|
||||
for entry in config_data[type_]:
|
||||
host = entry["host"]
|
||||
|
||||
if type_ == CONF_LIGHT:
|
||||
lights.append(SmartBulb(host))
|
||||
elif type_ == CONF_SWITCH:
|
||||
switches.append(SmartPlug(host))
|
||||
elif type_ == CONF_STRIP:
|
||||
try:
|
||||
ss_host = SmartStrip(host)
|
||||
except SmartDeviceException as sde:
|
||||
_LOGGER.error(
|
||||
"Failed to setup SmartStrip at %s: %s; not retrying", host, sde
|
||||
)
|
||||
continue
|
||||
for plug in ss_host.plugs.values():
|
||||
switches.append(plug)
|
||||
# Dimmers need to be defined as smart plugs to work correctly.
|
||||
elif type_ == CONF_DIMMER:
|
||||
lights.append(SmartPlug(host))
|
||||
|
||||
return SmartDevices(lights, switches)
|
||||
|
||||
|
||||
async def async_add_entities_retry(
|
||||
hass: HomeAssistantType,
|
||||
async_add_entities: Callable[[List[Any], bool], None],
|
||||
objects: List[Any],
|
||||
callback: Callable[[Any, Callable], None],
|
||||
interval: timedelta = timedelta(seconds=60),
|
||||
):
|
||||
"""
|
||||
Add entities now and retry later if issues are encountered.
|
||||
|
||||
If the callback throws an exception or returns false, that
|
||||
object will try again a while later.
|
||||
This is useful for devices that are not online when hass starts.
|
||||
:param hass:
|
||||
:param async_add_entities: The callback provided to a
|
||||
platform's async_setup.
|
||||
:param objects: The objects to create as entities.
|
||||
:param callback: The callback that will perform the add.
|
||||
:param interval: THe time between attempts to add.
|
||||
:return: A callback to cancel the retries.
|
||||
"""
|
||||
add_objects = objects.copy()
|
||||
|
||||
is_cancelled = False
|
||||
|
||||
def cancel_interval_callback():
|
||||
nonlocal is_cancelled
|
||||
is_cancelled = True
|
||||
|
||||
async def process_objects_loop(delay: int):
|
||||
if is_cancelled:
|
||||
return
|
||||
|
||||
await process_objects()
|
||||
|
||||
if not add_objects:
|
||||
return
|
||||
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
hass.async_create_task(process_objects_loop(delay))
|
||||
|
||||
async def process_objects(*args):
|
||||
# Process each object.
|
||||
for add_object in list(add_objects):
|
||||
# Call the individual item callback.
|
||||
try:
|
||||
_LOGGER.debug("Attempting to add object of type %s", type(add_object))
|
||||
result = await hass.async_add_job(
|
||||
callback, add_object, async_add_entities
|
||||
if type_ == CONF_LIGHT:
|
||||
lights.append(SmartBulb(host))
|
||||
elif type_ == CONF_SWITCH:
|
||||
switches.append(SmartPlug(host))
|
||||
elif type_ == CONF_STRIP:
|
||||
for plug in SmartStrip(host).plugs.values():
|
||||
switches.append(plug)
|
||||
# Dimmers need to be defined as smart plugs to work correctly.
|
||||
elif type_ == CONF_DIMMER:
|
||||
lights.append(SmartPlug(host))
|
||||
except SmartDeviceException as sde:
|
||||
_LOGGER.error(
|
||||
"Failed to setup device %s due to %s; not retrying", host, sde
|
||||
)
|
||||
except SmartDeviceException as ex:
|
||||
_LOGGER.debug(str(ex))
|
||||
result = False
|
||||
|
||||
if result is True or result is None:
|
||||
_LOGGER.debug("Added object")
|
||||
add_objects.remove(add_object)
|
||||
else:
|
||||
_LOGGER.debug("Failed to add object, will try again later")
|
||||
|
||||
await process_objects_loop(interval.seconds)
|
||||
|
||||
return cancel_interval_callback
|
||||
return SmartDevices(lights, switches)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue