Adjust Hue retry logic to changes in the aiohue library (#62665)

This commit is contained in:
Marcel van der Veldt 2021-12-23 14:04:10 +01:00 committed by GitHub
parent aa9746808e
commit 8e759bb267
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,13 +3,12 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable from collections.abc import Callable
from http import HTTPStatus
import logging import logging
from typing import Any from typing import Any
from aiohttp import client_exceptions from aiohttp import client_exceptions
from aiohue import HueBridgeV1, HueBridgeV2, LinkButtonNotPressed, Unauthorized from aiohue import HueBridgeV1, HueBridgeV2, LinkButtonNotPressed, Unauthorized
from aiohue.errors import AiohueException from aiohue.errors import AiohueException, BridgeBusy
import async_timeout import async_timeout
from homeassistant import core from homeassistant import core
@ -44,9 +43,6 @@ class HueBridge:
self.config_entry = config_entry self.config_entry = config_entry
self.hass = hass self.hass = hass
self.authorized = False self.authorized = False
self.parallel_updates_semaphore = asyncio.Semaphore(
3 if self.api_version == 1 else 10
)
# Jobs to be executed when API is reset. # Jobs to be executed when API is reset.
self.reset_jobs: list[core.CALLBACK_TYPE] = [] self.reset_jobs: list[core.CALLBACK_TYPE] = []
self.sensor_manager: SensorManager | None = None self.sensor_manager: SensorManager | None = None
@ -89,6 +85,7 @@ class HueBridge:
client_exceptions.ClientOSError, client_exceptions.ClientOSError,
client_exceptions.ServerDisconnectedError, client_exceptions.ServerDisconnectedError,
client_exceptions.ContentTypeError, client_exceptions.ContentTypeError,
BridgeBusy,
) as err: ) as err:
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
f"Error connecting to the Hue bridge at {self.host}" f"Error connecting to the Hue bridge at {self.host}"
@ -121,50 +118,19 @@ class HueBridge:
async def async_request_call( async def async_request_call(
self, task: Callable, *args, allowed_errors: list[str] | None = None, **kwargs self, task: Callable, *args, allowed_errors: list[str] | None = None, **kwargs
) -> Any: ) -> Any:
"""Limit parallel requests to Hue hub. """Send request to the Hue bridge, optionally omitting error(s)."""
The Hue hub can only handle a certain amount of parallel requests, total.
Although we limit our parallel requests, we still will run into issues because
other products are hitting up Hue.
ClientOSError means hub closed the socket on us.
ContentResponseError means hub raised an error.
Since we don't make bad requests, this is on them.
"""
max_tries = 5
async with self.parallel_updates_semaphore:
for tries in range(max_tries):
try: try:
return await task(*args, **kwargs) return await task(*args, **kwargs)
except AiohueException as err: except AiohueException as err:
# The new V2 api is a bit more fanatic with throwing errors # The (new) Hue api can be a bit fanatic with throwing errors
# some of which we accept in certain conditions # some of which we accept in certain conditions
# handle that here. Note that these errors are strings and do not have # handle that here. Note that these errors are strings and do not have
# an identifier or something. # an identifier or something.
if allowed_errors is not None and str(err) in allowed_errors: if allowed_errors is not None and str(err) in allowed_errors:
# log only # log only
self.logger.debug( self.logger.debug("Ignored error/warning from Hue API: %s", str(err))
"Ignored error/warning from Hue API: %s", str(err)
)
return None return None
raise err raise err
except (
client_exceptions.ClientOSError,
client_exceptions.ClientResponseError,
client_exceptions.ServerDisconnectedError,
) as err:
if tries == max_tries:
self.logger.error("Request failed %s times, giving up", tries)
raise
# We only retry if it's a server error. So raise on all 4XX errors.
if (
isinstance(err, client_exceptions.ClientResponseError)
and err.status < HTTPStatus.INTERNAL_SERVER_ERROR
):
raise
await asyncio.sleep(HUB_BUSY_SLEEP * tries)
async def async_reset(self) -> bool: async def async_reset(self) -> bool:
"""Reset this bridge to default state. """Reset this bridge to default state.