Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid blocking call #4

Merged
merged 2 commits into from
Sep 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions aiohuesyncbox/huesyncbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(
self._port = port
self._path = path

self._clientsession = self._get_clientsession()
self._clientsession: aiohttp.ClientSession | None = None

# API endpoints
self.behavior: Behavior
Expand All @@ -51,20 +51,30 @@ async def __aenter__(self):
async def __aexit__(self, exc_type, exc, tb):
await self.close()

def _get_clientsession(self) -> aiohttp.ClientSession:
async def _get_clientsession(self) -> aiohttp.ClientSession:
"""
Get a clientsession that is tuned for communication with the Hue Syncbox
"""
context = ssl.create_default_context(cadata=HSB_CACERT)
context.hostname_checks_common_name = True

def _build_ssl_context() -> ssl.SSLContext:
context = ssl.create_default_context(cadata=HSB_CACERT)
context.hostname_checks_common_name = True
return context


# Creating an SSL context has some blocking IO so need to run it in the executor
loop = asyncio.get_running_loop()
context = await loop.run_in_executor(None, _build_ssl_context)

connector = aiohttp.TCPConnector(
enable_cleanup_closed=True, # Home Assistant sets it so lets do it also
ssl=context,
limit_per_host=1, # Syncbox can handle a limited amount of connections, only take what we need
)

return aiohttp.ClientSession(connector=connector, timeout=aiohttp.ClientTimeout(total=10))
return aiohttp.ClientSession(
connector=connector, timeout=aiohttp.ClientTimeout(total=10)
)

@property
def access_token(self) -> str | None:
Expand Down Expand Up @@ -129,7 +139,8 @@ async def initialize(self):
)

async def close(self):
await self._clientsession.close()
if self._clientsession is not None:
await self._clientsession.close()

async def update(self):
response = await self.request("get", "")
Expand All @@ -147,6 +158,10 @@ async def request(
):
"""Make a request to the API."""

if self._clientsession is None:
self._clientsession = await self._get_clientsession()
assert(self._clientsession is not None)

if self._clientsession.closed:
# Avoid runtime errors when connection is closed.
# This solves an issue when Updates were scheduled and HA was shutdown
Expand Down Expand Up @@ -179,14 +194,10 @@ async def request(
return data
except aiohttp.ClientError as err:
logger.debug(err, exc_info=True)
raise RequestError(
f"Error requesting data from {self._host}"
) from err
raise RequestError(f"Error requesting data from {self._host}") from err
except asyncio.TimeoutError as err:
logger.debug(err, exc_info=True)
raise RequestError(
f"Timeout requesting data from {self._host}"
) from err
raise RequestError(f"Timeout requesting data from {self._host}") from err


def _raise_on_error(data: Dict):
Expand Down