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:
Angelo Gagliano 2020-10-11 15:10:36 -04:00 committed by GitHub
parent 366006e0aa
commit 874e1f6103
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 213 additions and 263 deletions

View file

@ -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)