From ae5606aa2f7895a888698484c29497532f96ccb9 Mon Sep 17 00:00:00 2001 From: Arie Catsman <120491684+catsmanac@users.noreply.github.com> Date: Sat, 7 Jun 2025 17:52:54 +0200 Subject: [PATCH] Migrate Enphase envoy from httpx to aiohttp (#146283) Co-authored-by: J. Nick Koston --- .../components/enphase_envoy/__init__.py | 25 +++---------------- .../components/enphase_envoy/config_flow.py | 4 +-- .../components/enphase_envoy/diagnostics.py | 9 ++++--- .../components/enphase_envoy/entity.py | 4 +-- .../components/enphase_envoy/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/enphase_envoy/conftest.py | 9 ++++--- 8 files changed, 22 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/enphase_envoy/__init__.py b/homeassistant/components/enphase_envoy/__init__.py index ba4aedf5013..eee6cb85e6d 100644 --- a/homeassistant/components/enphase_envoy/__init__.py +++ b/homeassistant/components/enphase_envoy/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations -import httpx from pyenphase import Envoy from homeassistant.config_entries import ConfigEntry @@ -10,14 +9,9 @@ from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr -from homeassistant.helpers.httpx_client import get_async_client +from homeassistant.helpers.aiohttp_client import async_create_clientsession -from .const import ( - DOMAIN, - OPTION_DISABLE_KEEP_ALIVE, - OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE, - PLATFORMS, -) +from .const import DOMAIN, PLATFORMS from .coordinator import EnphaseConfigEntry, EnphaseUpdateCoordinator @@ -25,19 +19,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> b """Set up Enphase Envoy from a config entry.""" host = entry.data[CONF_HOST] - options = entry.options - envoy = ( - Envoy( - host, - httpx.AsyncClient( - verify=False, limits=httpx.Limits(max_keepalive_connections=0) - ), - ) - if options.get( - OPTION_DISABLE_KEEP_ALIVE, OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE - ) - else Envoy(host, get_async_client(hass, verify_ssl=False)) - ) + session = async_create_clientsession(hass, verify_ssl=False) + envoy = Envoy(host, session) coordinator = EnphaseUpdateCoordinator(hass, envoy, entry) await coordinator.async_config_entry_first_refresh() diff --git a/homeassistant/components/enphase_envoy/config_flow.py b/homeassistant/components/enphase_envoy/config_flow.py index 5ee81dd8315..5b7bb98527c 100644 --- a/homeassistant/components/enphase_envoy/config_flow.py +++ b/homeassistant/components/enphase_envoy/config_flow.py @@ -24,7 +24,7 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.httpx_client import get_async_client +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo from homeassistant.helpers.typing import VolDictType @@ -63,7 +63,7 @@ async def validate_input( description_placeholders: dict[str, str], ) -> Envoy: """Validate the user input allows us to connect.""" - envoy = Envoy(host, get_async_client(hass, verify_ssl=False)) + envoy = Envoy(host, async_get_clientsession(hass, verify_ssl=False)) try: await envoy.setup() await envoy.authenticate(username=username, password=password) diff --git a/homeassistant/components/enphase_envoy/diagnostics.py b/homeassistant/components/enphase_envoy/diagnostics.py index 97079255876..e59a9fa09c5 100644 --- a/homeassistant/components/enphase_envoy/diagnostics.py +++ b/homeassistant/components/enphase_envoy/diagnostics.py @@ -6,6 +6,7 @@ import copy from datetime import datetime from typing import TYPE_CHECKING, Any +from aiohttp import ClientResponse from attr import asdict from pyenphase.envoy import Envoy from pyenphase.exceptions import EnvoyError @@ -69,14 +70,14 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]: for end_point in end_points: try: - response = await envoy.request(end_point) - fixture_data[end_point] = response.text.replace("\n", "").replace( - serial, CLEAN_TEXT + response: ClientResponse = await envoy.request(end_point) + fixture_data[end_point] = ( + (await response.text()).replace("\n", "").replace(serial, CLEAN_TEXT) ) fixture_data[f"{end_point}_log"] = json_dumps( { "headers": dict(response.headers.items()), - "code": response.status_code, + "code": response.status, } ) except EnvoyError as err: diff --git a/homeassistant/components/enphase_envoy/entity.py b/homeassistant/components/enphase_envoy/entity.py index 04987d861d2..32be5ec8b8b 100644 --- a/homeassistant/components/enphase_envoy/entity.py +++ b/homeassistant/components/enphase_envoy/entity.py @@ -5,7 +5,7 @@ from __future__ import annotations from collections.abc import Callable, Coroutine from typing import Any, Concatenate -from httpx import HTTPError +from aiohttp import ClientError from pyenphase import EnvoyData from pyenphase.exceptions import EnvoyError @@ -16,7 +16,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN from .coordinator import EnphaseUpdateCoordinator -ACTIONERRORS = (EnvoyError, HTTPError) +ACTIONERRORS = (EnvoyError, ClientError) class EnvoyBaseEntity(CoordinatorEntity[EnphaseUpdateCoordinator]): diff --git a/homeassistant/components/enphase_envoy/manifest.json b/homeassistant/components/enphase_envoy/manifest.json index e978ded7321..6f1e0a943ef 100644 --- a/homeassistant/components/enphase_envoy/manifest.json +++ b/homeassistant/components/enphase_envoy/manifest.json @@ -7,7 +7,7 @@ "iot_class": "local_polling", "loggers": ["pyenphase"], "quality_scale": "platinum", - "requirements": ["pyenphase==1.26.1"], + "requirements": ["pyenphase==2.0.1"], "zeroconf": [ { "type": "_enphase-envoy._tcp.local." diff --git a/requirements_all.txt b/requirements_all.txt index 8f42a59a6ff..ff953742dde 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1958,7 +1958,7 @@ pyeiscp==0.0.7 pyemoncms==0.1.1 # homeassistant.components.enphase_envoy -pyenphase==1.26.1 +pyenphase==2.0.1 # homeassistant.components.envisalink pyenvisalink==4.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b24196bb49b..97485cd1af2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1630,7 +1630,7 @@ pyeiscp==0.0.7 pyemoncms==0.1.1 # homeassistant.components.enphase_envoy -pyenphase==1.26.1 +pyenphase==2.0.1 # homeassistant.components.everlights pyeverlights==0.1.0 diff --git a/tests/components/enphase_envoy/conftest.py b/tests/components/enphase_envoy/conftest.py index 89a0e9b4610..7ad15f85ac2 100644 --- a/tests/components/enphase_envoy/conftest.py +++ b/tests/components/enphase_envoy/conftest.py @@ -5,6 +5,7 @@ from typing import Any from unittest.mock import AsyncMock, Mock, patch import jwt +import multidict from pyenphase import ( EnvoyACBPower, EnvoyBatteryAggregate, @@ -101,9 +102,11 @@ async def mock_envoy( mock_envoy.auth = EnvoyTokenAuth("127.0.0.1", token=token, envoy_serial="1234") mock_envoy.serial_number = "1234" mock = Mock() - mock.status_code = 200 - mock.text = "Testing request \nreplies." - mock.headers = {"Hello": "World"} + mock.status = 200 + aiohttp_text = AsyncMock() + aiohttp_text.return_value = "Testing request \nreplies." + mock.text = aiohttp_text + mock.headers = multidict.MultiDict([("Hello", "World")]) mock_envoy.request.return_value = mock # determine fixture file name, default envoy if no request passed