Limit rainbird aiohttp client session to a single connection (#112146)
Limit rainbird to a single open http connectionpull/112516/head
parent
93ee900cb3
commit
274ab2328e
|
@ -11,11 +11,10 @@ from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from .const import CONF_SERIAL_NUMBER
|
||||
from .coordinator import RainbirdData
|
||||
from .coordinator import RainbirdData, async_create_clientsession
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -36,9 +35,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
clientsession = async_create_clientsession()
|
||||
entry.async_on_unload(clientsession.close)
|
||||
controller = AsyncRainbirdController(
|
||||
AsyncRainbirdClient(
|
||||
async_get_clientsession(hass),
|
||||
clientsession,
|
||||
entry.data[CONF_HOST],
|
||||
entry.data[CONF_PASSWORD],
|
||||
)
|
||||
|
|
|
@ -20,7 +20,6 @@ from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD
|
|||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import config_validation as cv, selector
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from .const import (
|
||||
|
@ -30,6 +29,7 @@ from .const import (
|
|||
DOMAIN,
|
||||
TIMEOUT_SECONDS,
|
||||
)
|
||||
from .coordinator import async_create_clientsession
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -101,9 +101,10 @@ class RainbirdConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
Raises a ConfigFlowError on failure.
|
||||
"""
|
||||
clientsession = async_create_clientsession()
|
||||
controller = AsyncRainbirdController(
|
||||
AsyncRainbirdClient(
|
||||
async_get_clientsession(self.hass),
|
||||
clientsession,
|
||||
host,
|
||||
password,
|
||||
)
|
||||
|
@ -124,6 +125,8 @@ class RainbirdConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
f"Error connecting to Rain Bird controller: {str(err)}",
|
||||
"cannot_connect",
|
||||
) from err
|
||||
finally:
|
||||
await clientsession.close()
|
||||
|
||||
async def async_finish(
|
||||
self,
|
||||
|
|
|
@ -9,6 +9,7 @@ from functools import cached_property
|
|||
import logging
|
||||
from typing import TypeVar
|
||||
|
||||
import aiohttp
|
||||
from pyrainbird.async_client import (
|
||||
AsyncRainbirdController,
|
||||
RainbirdApiException,
|
||||
|
@ -28,6 +29,9 @@ UPDATE_INTERVAL = datetime.timedelta(minutes=1)
|
|||
# changes, so we refresh it less often.
|
||||
CALENDAR_UPDATE_INTERVAL = datetime.timedelta(minutes=15)
|
||||
|
||||
# Rainbird devices can only accept a single request at a time
|
||||
CONECTION_LIMIT = 1
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
@ -43,6 +47,13 @@ class RainbirdDeviceState:
|
|||
rain_delay: int
|
||||
|
||||
|
||||
def async_create_clientsession() -> aiohttp.ClientSession:
|
||||
"""Create a rainbird async_create_clientsession with a connection limit."""
|
||||
return aiohttp.ClientSession(
|
||||
connector=aiohttp.TCPConnector(limit=CONECTION_LIMIT),
|
||||
)
|
||||
|
||||
|
||||
class RainbirdUpdateCoordinator(DataUpdateCoordinator[RainbirdDeviceState]):
|
||||
"""Coordinator for rainbird API calls."""
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
from typing import Any
|
||||
|
@ -15,7 +16,7 @@ from homeassistant.components.rainbird.const import (
|
|||
ATTR_DURATION,
|
||||
DEFAULT_TRIGGER_TIME_MINUTES,
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
@ -155,6 +156,31 @@ def setup_platforms(
|
|||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def aioclient_mock(hass: HomeAssistant) -> Generator[AiohttpClientMocker, None, None]:
|
||||
"""Context manager to mock aiohttp client."""
|
||||
mocker = AiohttpClientMocker()
|
||||
|
||||
def create_session():
|
||||
session = mocker.create_session(hass.loop)
|
||||
|
||||
async def close_session(event):
|
||||
"""Close session."""
|
||||
await session.close()
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, close_session)
|
||||
return session
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.rainbird.async_create_clientsession",
|
||||
side_effect=create_session,
|
||||
), patch(
|
||||
"homeassistant.components.rainbird.config_flow.async_create_clientsession",
|
||||
side_effect=create_session,
|
||||
):
|
||||
yield mocker
|
||||
|
||||
|
||||
def rainbird_json_response(result: dict[str, str]) -> bytes:
|
||||
"""Create a fake API response."""
|
||||
return encryption.encrypt(
|
||||
|
|
Loading…
Reference in New Issue