From d3fe434c719410a6ae28a125077cceb030ead815 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sat, 4 Jul 2026 19:20:28 +0200 Subject: [PATCH 1/7] Fix BasicAuth depreciation --- plugwise/smilecomm.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/plugwise/smilecomm.py b/plugwise/smilecomm.py index 61617a7e4..07746f310 100644 --- a/plugwise/smilecomm.py +++ b/plugwise/smilecomm.py @@ -15,7 +15,7 @@ from plugwise.util import escape_illegal_xml_characters # This way of importing aiohttp is because of patch/mocking in testing (aiohttp timeouts) -from aiohttp import BasicAuth, ClientError, ClientResponse, ClientSession, ClientTimeout +import aiohttp from defusedxml import ElementTree as etree @@ -29,12 +29,12 @@ def __init__( port: int, timeout: int, username: str, - websession: ClientSession | None, + websession: aiohttp.ClientSession | None, ) -> None: """Set the constructor for this class.""" if not websession: - aio_timeout = ClientTimeout(total=timeout) - self._websession = ClientSession(timeout=aio_timeout) + aio_timeout = aiohttp.ClientTimeout(total=timeout) + self._websession = aiohttp.ClientSession(timeout=aio_timeout) else: self._websession = websession @@ -42,7 +42,7 @@ def __init__( if host.count(":") > 2: # pragma: no cover host = f"[{host}]" - self._auth = BasicAuth(username, password=password) + self._base_header = {"Authorization": aiohttp.encode_basic_auth(username, password=password)} self._endpoint = f"http://{host}:{str(port)}" # Sensitive async def _request( @@ -53,36 +53,32 @@ async def _request( data: str | None = None, ) -> etree.Element: """Get/put/delete data from a give URL.""" - resp: ClientResponse + resp: aiohttp.ClientResponse url = f"{self._endpoint}{command}" try: match method: case "delete": - resp = await self._websession.delete(url, auth=self._auth) + resp = await self._websession.delete(url, headers=self._base_header) case "get": # Work-around for Stretchv2, should not hurt the other smiles - headers = {"Accept-Encoding": "gzip"} - resp = await self._websession.get( - url, headers=headers, auth=self._auth - ) + headers = {**self._base_header, "Accept-Encoding": "gzip"} + resp = await self._websession.get(url, headers=headers) case "post": - headers = {"Content-type": "text/xml"} + headers = {**self._base_header, "Content-type": "text/xml"} resp = await self._websession.post( url, headers=headers, data=data, - auth=self._auth, ) case "put": - headers = {"Content-type": "text/xml"} + headers = {**self._base_header, "Content-type": "text/xml"} resp = await self._websession.put( url, headers=headers, data=data, - auth=self._auth, ) except ( - ClientError + aiohttp.ClientError ) as exc: # ClientError is an ancestor class of ServerTimeoutError if retry < 1: LOGGER.warning( @@ -108,7 +104,7 @@ async def _request( return await self._request_validate(resp, method) async def _request_validate( - self, resp: ClientResponse, method: str + self, resp: aiohttp.ClientResponse, method: str ) -> etree.Element: """Helper-function for _request(): validate the returned data.""" match resp.status: From 89c00ea5409e6fc57e1861b366c0cf56f2fdb17c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 08:21:54 +0200 Subject: [PATCH 2/7] Update test_generic --- tests/test_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generic.py b/tests/test_generic.py index 4a6af75c0..6704ad6d6 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -48,7 +48,7 @@ async def test_connect_fail_firmware(self): # Test connect for timeout @patch( - "plugwise.smilecomm.ClientSession.get", + "plugwise.smilecomm.aiohttp.ClientSession.get", side_effect=aiohttp.ServerTimeoutError, ) @pytest.mark.asyncio From 5225ef47d23f21979ed91bcff218cec4f62c7327 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 08:22:36 +0200 Subject: [PATCH 3/7] Ruffed --- plugwise/smilecomm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise/smilecomm.py b/plugwise/smilecomm.py index 07746f310..3fd226d83 100644 --- a/plugwise/smilecomm.py +++ b/plugwise/smilecomm.py @@ -42,7 +42,9 @@ def __init__( if host.count(":") > 2: # pragma: no cover host = f"[{host}]" - self._base_header = {"Authorization": aiohttp.encode_basic_auth(username, password=password)} + self._base_header = { + "Authorization": aiohttp.encode_basic_auth(username, password=password) + } self._endpoint = f"http://{host}:{str(port)}" # Sensitive async def _request( From 9be2b716782c2a9aa6d3bddf2fb4ead6e2de448e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 08:30:01 +0200 Subject: [PATCH 4/7] Revert to required imports --- plugwise/smilecomm.py | 22 ++++++++++++++-------- tests/test_generic.py | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/plugwise/smilecomm.py b/plugwise/smilecomm.py index 3fd226d83..7d3425587 100644 --- a/plugwise/smilecomm.py +++ b/plugwise/smilecomm.py @@ -15,7 +15,13 @@ from plugwise.util import escape_illegal_xml_characters # This way of importing aiohttp is because of patch/mocking in testing (aiohttp timeouts) -import aiohttp +from aiohttp import ( + ClientError, + ClientResponse, + ClientSession, + ClientTimeout, + encode_basic_auth, +) from defusedxml import ElementTree as etree @@ -29,12 +35,12 @@ def __init__( port: int, timeout: int, username: str, - websession: aiohttp.ClientSession | None, + websession: ClientSession | None, ) -> None: """Set the constructor for this class.""" if not websession: - aio_timeout = aiohttp.ClientTimeout(total=timeout) - self._websession = aiohttp.ClientSession(timeout=aio_timeout) + aio_timeout = ClientTimeout(total=timeout) + self._websession = ClientSession(timeout=aio_timeout) else: self._websession = websession @@ -43,7 +49,7 @@ def __init__( host = f"[{host}]" self._base_header = { - "Authorization": aiohttp.encode_basic_auth(username, password=password) + "Authorization": encode_basic_auth(username, password=password) } self._endpoint = f"http://{host}:{str(port)}" # Sensitive @@ -55,7 +61,7 @@ async def _request( data: str | None = None, ) -> etree.Element: """Get/put/delete data from a give URL.""" - resp: aiohttp.ClientResponse + resp: ClientResponse url = f"{self._endpoint}{command}" try: match method: @@ -80,7 +86,7 @@ async def _request( data=data, ) except ( - aiohttp.ClientError + ClientError ) as exc: # ClientError is an ancestor class of ServerTimeoutError if retry < 1: LOGGER.warning( @@ -106,7 +112,7 @@ async def _request( return await self._request_validate(resp, method) async def _request_validate( - self, resp: aiohttp.ClientResponse, method: str + self, resp: ClientResponse, method: str ) -> etree.Element: """Helper-function for _request(): validate the returned data.""" match resp.status: diff --git a/tests/test_generic.py b/tests/test_generic.py index 6704ad6d6..4a6af75c0 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -48,7 +48,7 @@ async def test_connect_fail_firmware(self): # Test connect for timeout @patch( - "plugwise.smilecomm.aiohttp.ClientSession.get", + "plugwise.smilecomm.ClientSession.get", side_effect=aiohttp.ServerTimeoutError, ) @pytest.mark.asyncio From 096c7a136a6da4e3c8fd177094b1af0e3d70f23a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 08:33:15 +0200 Subject: [PATCH 5/7] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aacc7d943..6ab0da59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Add PEP 740 digital attestations (workaround until included in `uv publish`) - Pin GitHub acions including our own from gh-actions - +- Solve aiohttp BasicAuth deprecation via PR [#890](https://github.com/plugwise/python-plugwise/pull/890) ## v1.12.0 - Replace the DHW-comfort-mode switch by a DHW mode selector to match the new HA select or water_heater platform updates, via PR [#883](https://github.com/plugwise/python-plugwise/pull/883) From c9fc54fa54e28a998b0e1ddd20b1f0a4535620cc Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 08:40:00 +0200 Subject: [PATCH 6/7] Update pyproject.toml as suggested --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 997b1731c..c4b0ded55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ maintainers = [ requires-python = ">=3.13" dependencies = [ "aiofiles", - "aiohttp", + "aiohttp>=3.14", "defusedxml", "munch", "python-dateutil", From 123fbc301fc0c0f5619a56c1d369b6e0da720667 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Sun, 5 Jul 2026 15:17:02 +0200 Subject: [PATCH 7/7] Rework CHANGELOG after rebase --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab0da59a..ab9c0b3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ## Ongoing -- Add PEP 740 digital attestations (workaround until included in `uv publish`) -- Pin GitHub acions including our own from gh-actions +- Add PEP 740 digital attestations (workaround until included in `uv publish`), pin GitHub acions including our own from gh-actions, via PR [#891](https://github.com/plugwise/python-plugwise/pull/891) - Solve aiohttp BasicAuth deprecation via PR [#890](https://github.com/plugwise/python-plugwise/pull/890) + ## v1.12.0 - Replace the DHW-comfort-mode switch by a DHW mode selector to match the new HA select or water_heater platform updates, via PR [#883](https://github.com/plugwise/python-plugwise/pull/883)