From 34c6ca52c83889cbf4ed9ff2484b270cb42cba0e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 6 Jul 2026 02:03:18 -0400 Subject: [PATCH] Fix minor issues in update and services (NEW-3..NEW-6) - NEW-3: default _attr_release_notes at class level so async_release_notes() can't raise AttributeError before/without a successful GitHub fetch. - NEW-4: import ColorScheme from opendisplay (re-exported) instead of the undeclared direct epaper_dithering dependency. - NEW-5: add explicit aiohttp.ClientTimeout to all external HTTP calls (GitHub metadata 30s, firmware download 120s, media download 30s). - NEW-6: run blocking os.path.isdir font-dir checks in the executor. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012g2e8mr132vcizx92WsgiR --- custom_components/opendisplay/services.py | 8 +++++--- custom_components/opendisplay/update.py | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/custom_components/opendisplay/services.py b/custom_components/opendisplay/services.py index 6ca5651..0765668 100644 --- a/custom_components/opendisplay/services.py +++ b/custom_components/opendisplay/services.py @@ -11,11 +11,11 @@ from typing import TYPE_CHECKING, Any import aiohttp -from epaper_dithering import ColorScheme from odl_renderer import generate_image from opendisplay import ( AuthenticationFailedError, AuthenticationRequiredError, + ColorScheme, DitherMode, FitMode, LedFlashConfig, @@ -261,7 +261,9 @@ async def _async_download_image(hass: HomeAssistant, url: str) -> PILImage.Image ) session = async_get_clientsession(hass) try: - async with session.get(url) as resp: + async with session.get( + url, timeout=aiohttp.ClientTimeout(total=30) + ) as resp: resp.raise_for_status() data = await resp.read() except aiohttp.ClientError as err: @@ -590,7 +592,7 @@ async def _drawcustom_for_device( accent_color=color_scheme.accent_color, session=async_get_clientsession(hass), data_provider=HADataProvider(hass), - font_dirs=_font_search_dirs(hass), + font_dirs=await hass.async_add_executor_job(_font_search_dirs, hass), ) if call.data["dry-run"]: diff --git a/custom_components/opendisplay/update.py b/custom_components/opendisplay/update.py index 2937aa1..4e9fef9 100644 --- a/custom_components/opendisplay/update.py +++ b/custom_components/opendisplay/update.py @@ -83,6 +83,7 @@ class OpenDisplayFirmwareUpdateEntity(OpenDisplayEntity[UpdateEntityDescription] """Firmware update entity for an OpenDisplay device.""" _attr_latest_version: str | None = None + _attr_release_notes: str | None = None should_poll = True # override coordinator's should_poll=False; GitHub needs regular polling def __init__(self, coordinator, entry: OpenDisplayConfigEntry) -> None: @@ -145,6 +146,7 @@ async def async_update(self) -> None: async with session.get( _GITHUB_LATEST.format(repo=self._firmware_repo), headers=_GITHUB_HEADERS, + timeout=aiohttp.ClientTimeout(total=30), ) as resp: resp.raise_for_status() data = await resp.json() @@ -258,6 +260,7 @@ async def _download_asset(self, tag: str, asset_name: str) -> bytes: async with session.get( _GITHUB_RELEASE.format(repo=self._firmware_repo, tag=tag), headers=_GITHUB_HEADERS, + timeout=aiohttp.ClientTimeout(total=30), ) as resp: resp.raise_for_status() release = await resp.json() @@ -276,7 +279,9 @@ async def _download_asset(self, tag: str, asset_name: str) -> bytes: _LOGGER.debug("Downloading %s from %s", asset_name, download_url) try: - async with session.get(download_url) as resp: + async with session.get( + download_url, timeout=aiohttp.ClientTimeout(total=120) + ) as resp: resp.raise_for_status() return await resp.read() except aiohttp.ClientError as err: