From d77e28ad23f71947a1845b863bf1e6dbed6e8a68 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:48:31 -0400 Subject: [PATCH 1/5] Add auth.py + test New class to wrap the auth for python-hilo --- custom_components/hilo/api.py | 17 +++++++++++++++++ tests/test_auth.py | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 custom_components/hilo/api.py create mode 100644 tests/test_auth.py diff --git a/custom_components/hilo/api.py b/custom_components/hilo/api.py new file mode 100644 index 00000000..b9414c15 --- /dev/null +++ b/custom_components/hilo/api.py @@ -0,0 +1,17 @@ +"""API for Hilo bound to Home Assistant OAuth.""" + +from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session +from pyhilo import AbstractAuth + + +class AsyncConfigEntryAuth(AbstractAuth): + """Provide Hilo authentication tied to an OAuth2 based config entry.""" + + def __init__(self, oauth_session: OAuth2Session) -> None: + """Initialize.""" + self._oauth_session = oauth_session + + async def async_get_access_token(self) -> str: + """Return a valid access token, refreshing it first if it expired.""" + await self._oauth_session.async_ensure_token_valid() + return str(self._oauth_session.token["access_token"]) diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 00000000..8365b029 --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,19 @@ +"""Tests for the Hilo auth bridge.""" + +from unittest.mock import AsyncMock, MagicMock + +from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session + +from custom_components.hilo.auth import HiloAuth + + +async def test_access_token_refreshed_before_return() -> None: + """The session is asked to refresh before its token is read.""" + oauth_session = MagicMock(spec=OAuth2Session) + oauth_session.async_ensure_token_valid = AsyncMock() + oauth_session.token = {"access_token": "fresh-token"} + + token = await HiloAuth(oauth_session).async_get_access_token() + + oauth_session.async_ensure_token_valid.assert_awaited_once() + assert token == "fresh-token" From e00d7722c1b7d3df5cb1d2f042f90833e887030b Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:53:20 -0400 Subject: [PATCH 2/5] Add import and call new class --- custom_components/hilo/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/custom_components/hilo/__init__.py b/custom_components/hilo/__init__.py index d2ff91e3..b7da1adf 100644 --- a/custom_components/hilo/__init__.py +++ b/custom_components/hilo/__init__.py @@ -48,6 +48,7 @@ from pyhilo.util import from_utc_timestamp, time_diff from pysignalr.exceptions import ServerError as SignalRServerError +from .api import HiloAuth from .config_flow import STEP_OPTION_SCHEMA, HiloFlowHandler from .const import ( CONF_APPRECIATION_PHASE, @@ -212,8 +213,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: HiloConfigEntry) -> bool session=async_create_clientsession( hass, cookie_jar=CookieJar(quote_cookie=False) ), - oauth_session=config_entry_oauth2_flow.OAuth2Session( - hass, entry, implementation + auth=HiloAuth( + config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) ), log_traces=current_options.get(CONF_LOG_TRACES, DEFAULT_LOG_TRACES), state_yaml=hass.config.path("hilo_state.yaml"), From df0f1f7eb6a4b4ed986be7745d63431b5446152c Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:59:24 -0400 Subject: [PATCH 3/5] Quality scale --- custom_components/hilo/api.py | 2 +- custom_components/hilo/quality_scale.yaml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/custom_components/hilo/api.py b/custom_components/hilo/api.py index b9414c15..657e794a 100644 --- a/custom_components/hilo/api.py +++ b/custom_components/hilo/api.py @@ -4,7 +4,7 @@ from pyhilo import AbstractAuth -class AsyncConfigEntryAuth(AbstractAuth): +class HiloAuth(AbstractAuth): """Provide Hilo authentication tied to an OAuth2 based config entry.""" def __init__(self, oauth_session: OAuth2Session) -> None: diff --git a/custom_components/hilo/quality_scale.yaml b/custom_components/hilo/quality_scale.yaml index 24941fb5..93493849 100644 --- a/custom_components/hilo/quality_scale.yaml +++ b/custom_components/hilo/quality_scale.yaml @@ -14,9 +14,7 @@ rules: comment: entity.py exists; coordinator.py does not. config-flow-test-coverage: todo config-flow: done - dependency-transparency: - status: todo - comment: python-hilo imports homeassistant; fixed by python-hilo PR 460. + dependency-transparency: done docs-actions: status: exempt comment: The integration does not register actions. From 595dc00c4c705f3b4d2a4667b6fc64e3326e2138 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:23:30 -0400 Subject: [PATCH 4/5] Rename test --- tests/{test_auth.py => test_api.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{test_auth.py => test_api.py} (92%) diff --git a/tests/test_auth.py b/tests/test_api.py similarity index 92% rename from tests/test_auth.py rename to tests/test_api.py index 8365b029..1007f146 100644 --- a/tests/test_auth.py +++ b/tests/test_api.py @@ -4,7 +4,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session -from custom_components.hilo.auth import HiloAuth +from custom_components.hilo.api import HiloAuth async def test_access_token_refreshed_before_return() -> None: From b69cfe3b086d263398312bee335bc1bb7b668658 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:25:31 -0400 Subject: [PATCH 5/5] Bump python-hilo to 2026.10.2 HiloAuth implements the AbstractAuth interface, which first ships in python-hilo 2026.10.2. With the old pin, CI installed 2026.10.1 and every test failed at import time while loading conftest. Co-Authored-By: Claude Opus 5.5 --- custom_components/hilo/manifest.json | 2 +- pyproject.toml | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/custom_components/hilo/manifest.json b/custom_components/hilo/manifest.json index a33a7735..213c9ce7 100755 --- a/custom_components/hilo/manifest.json +++ b/custom_components/hilo/manifest.json @@ -11,6 +11,6 @@ "documentation": "https://github.com/dvd-dev/hilo", "iot_class": "cloud_push", "issue_tracker": "https://github.com/dvd-dev/hilo/issues", - "requirements": ["python-hilo==2026.10.1"], + "requirements": ["python-hilo==2026.10.2"], "version": "2026.10.3" } diff --git a/pyproject.toml b/pyproject.toml index 5b3f08b9..3a5f39d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ test = [ "pytest", "pytest-cov", "pytest-homeassistant-custom-component>=0.13.0", - "python-hilo==2026.10.1", + "python-hilo==2026.10.2", "bandit>=1.7.1", "ruff>=0.17.2", "pre-commit>=4.7.0", diff --git a/uv.lock b/uv.lock index 1eb1b215..18734ed4 100644 --- a/uv.lock +++ b/uv.lock @@ -2612,7 +2612,7 @@ test = [ { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-homeassistant-custom-component", specifier = ">=0.13.0" }, - { name = "python-hilo", specifier = "==2026.10.1" }, + { name = "python-hilo", specifier = "==2026.10.2" }, { name = "ruff", specifier = ">=0.17.2" }, ] @@ -5469,7 +5469,7 @@ wheels = [ [[package]] name = "python-hilo" -version = "2026.10.1" +version = "2026.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiofiles" }, @@ -5484,9 +5484,9 @@ dependencies = [ { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13.2'" }, { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13.2'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/e4/0d43bb9bc2804c3e475dfba843580c0567ee6ef95eb00b8bfc9f15c62aa0/python_hilo-2026.10.1.tar.gz", hash = "sha256:a4f625874449c99220b435a497fd3694259c9555d4a0f128d24812111e928adf", size = 170699, upload-time = "2026-10-12T01:16:33.943Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/88/e6f4c7126053ef19773dd6281a05d687067a1df0b315c86b36a999f65836/python_hilo-2026.10.2.tar.gz", hash = "sha256:1b94d492c6330f6028f8b899bba1c16c7ddb2560be3aefb8e5215e064ec24a6f", size = 139070, upload-time = "2026-10-27T23:23:13.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/2f/693f5ed2d07ff0e04ea0155157bfeb238c39c5d6dc82786b9fc76a727916/python_hilo-2026.10.1-py3-none-any.whl", hash = "sha256:218043d7a37c541686f9491acdf1c2e40f6868fb5b8310cb20d04f974145ba6e", size = 39657, upload-time = "2026-10-12T01:16:32.794Z" }, + { url = "https://files.pythonhosted.org/packages/03/63/52b5b96a493c5580da58572ea49a386c34e4f0f5b963eeb6e9eee11c8309/python_hilo-2026.10.2-py3-none-any.whl", hash = "sha256:356707e875bf5a190ba07a3d4d697e011a781dbd3cec8ba4d3c865b64127887f", size = 40610, upload-time = "2026-10-27T23:23:12.14Z" }, ] [[package]]