diff --git a/aiohasupervisor/addons.py b/aiohasupervisor/addons.py index 0e85ca2..b60e0b5 100644 --- a/aiohasupervisor/addons.py +++ b/aiohasupervisor/addons.py @@ -3,7 +3,7 @@ from typing import Any from .client import _SupervisorComponentClient -from .const import ResponseType +from .const import TIMEOUT_60_SECONDS, ResponseType from .models.addons import ( AddonsConfigValidate, AddonsList, @@ -38,19 +38,20 @@ async def uninstall_addon( await self._client.post( f"addons/{addon}/uninstall", json=options.to_dict() if options else None, + timeout=TIMEOUT_60_SECONDS, ) async def start_addon(self, addon: str) -> None: """Start an addon.""" - await self._client.post(f"addons/{addon}/start") + await self._client.post(f"addons/{addon}/start", timeout=TIMEOUT_60_SECONDS) async def stop_addon(self, addon: str) -> None: """Stop an addon.""" - await self._client.post(f"addons/{addon}/stop") + await self._client.post(f"addons/{addon}/stop", timeout=TIMEOUT_60_SECONDS) async def restart_addon(self, addon: str) -> None: """Restart an addon.""" - await self._client.post(f"addons/{addon}/restart") + await self._client.post(f"addons/{addon}/restart", timeout=None) async def addon_options(self, addon: str, options: AddonsOptions) -> None: """Set options for addon.""" diff --git a/aiohasupervisor/backups.py b/aiohasupervisor/backups.py index 4e5e354..f817789 100644 --- a/aiohasupervisor/backups.py +++ b/aiohasupervisor/backups.py @@ -55,6 +55,7 @@ async def full_backup(self, options: FullBackupOptions | None = None) -> NewBack "backups/new/full", json=options.to_dict() if options else None, response_type=ResponseType.JSON, + timeout=None, ) return NewBackup.from_dict(result.data) @@ -64,6 +65,7 @@ async def partial_backup(self, options: PartialBackupOptions) -> NewBackup: "backups/new/partial", json=options.to_dict(), response_type=ResponseType.JSON, + timeout=None, ) return NewBackup.from_dict(result.data) @@ -84,6 +86,7 @@ async def full_restore( f"backups/{backup}/restore/full", json=options.to_dict() if options else None, response_type=ResponseType.JSON, + timeout=None, ) return BackupJob.from_dict(result.data) @@ -95,6 +98,7 @@ async def partial_restore( f"backups/{backup}/restore/partial", json=options.to_dict(), response_type=ResponseType.JSON, + timeout=None, ) return BackupJob.from_dict(result.data) diff --git a/aiohasupervisor/client.py b/aiohasupervisor/client.py index a0d0c6e..7c9c396 100644 --- a/aiohasupervisor/client.py +++ b/aiohasupervisor/client.py @@ -14,7 +14,7 @@ ) from yarl import URL -from .const import ResponseType +from .const import DEFAULT_TIMEOUT, ResponseType from .exceptions import ( SupervisorAuthenticationError, SupervisorBadRequestError, @@ -50,15 +50,9 @@ class _SupervisorClient: api_host: str token: str - request_timeout: int session: ClientSession | None = None _close_session: bool = field(default=False, init=False) - @property - def timeout(self) -> ClientTimeout: - """Timeout for requests.""" - return ClientTimeout(total=self.request_timeout) - async def _request( self, method: HTTPMethod, @@ -68,6 +62,7 @@ async def _request( response_type: ResponseType, json: dict[str, Any] | None = None, data: Any = None, + timeout: ClientTimeout | None = DEFAULT_TIMEOUT, ) -> Response: """Handle a request to Supervisor.""" try: @@ -102,7 +97,7 @@ async def _request( async with self.session.request( method.value, url, - timeout=self.timeout, + timeout=timeout, headers=headers, params=params, json=json, @@ -153,6 +148,7 @@ async def get( *, params: dict[str, str] | None = None, response_type: ResponseType = ResponseType.JSON, + timeout: ClientTimeout | None = DEFAULT_TIMEOUT, ) -> Response: """Handle a GET request to Supervisor.""" return await self._request( @@ -160,6 +156,7 @@ async def get( uri, params=params, response_type=response_type, + timeout=timeout, ) async def post( @@ -170,6 +167,7 @@ async def post( response_type: ResponseType = ResponseType.NONE, json: dict[str, Any] | None = None, data: Any = None, + timeout: ClientTimeout | None = DEFAULT_TIMEOUT, ) -> Response: """Handle a POST request to Supervisor.""" return await self._request( @@ -179,6 +177,7 @@ async def post( response_type=response_type, json=json, data=data, + timeout=timeout, ) async def put( @@ -187,6 +186,7 @@ async def put( *, params: dict[str, str] | None = None, json: dict[str, Any] | None = None, + timeout: ClientTimeout | None = DEFAULT_TIMEOUT, ) -> Response: """Handle a PUT request to Supervisor.""" return await self._request( @@ -195,6 +195,7 @@ async def put( params=params, response_type=ResponseType.NONE, json=json, + timeout=timeout, ) async def delete( @@ -202,6 +203,7 @@ async def delete( uri: str, *, params: dict[str, str] | None = None, + timeout: ClientTimeout | None = DEFAULT_TIMEOUT, ) -> Response: """Handle a DELETE request to Supervisor.""" return await self._request( @@ -209,6 +211,7 @@ async def delete( uri, params=params, response_type=ResponseType.NONE, + timeout=timeout, ) async def close(self) -> None: diff --git a/aiohasupervisor/const.py b/aiohasupervisor/const.py index 852b68b..a5450ae 100644 --- a/aiohasupervisor/const.py +++ b/aiohasupervisor/const.py @@ -2,6 +2,11 @@ from enum import StrEnum +from aiohttp import ClientTimeout + +DEFAULT_TIMEOUT = ClientTimeout(total=10) +TIMEOUT_60_SECONDS = ClientTimeout(total=60) + class ResponseType(StrEnum): """Expected response type.""" diff --git a/aiohasupervisor/discovery.py b/aiohasupervisor/discovery.py index 4fe002a..14dcd75 100644 --- a/aiohasupervisor/discovery.py +++ b/aiohasupervisor/discovery.py @@ -3,7 +3,7 @@ from uuid import UUID from .client import _SupervisorComponentClient -from .const import ResponseType +from .const import TIMEOUT_60_SECONDS, ResponseType from .models.discovery import Discovery, DiscoveryConfig, DiscoveryList, SetDiscovery @@ -12,7 +12,7 @@ class DiscoveryClient(_SupervisorComponentClient): async def list(self) -> list[Discovery]: """List discovered active services.""" - result = await self._client.get("discovery") + result = await self._client.get("discovery", timeout=TIMEOUT_60_SECONDS) return DiscoveryList.from_dict(result.data).discovery async def get(self, uuid: UUID) -> Discovery: diff --git a/aiohasupervisor/homeassistant.py b/aiohasupervisor/homeassistant.py index 392b150..1422c75 100644 --- a/aiohasupervisor/homeassistant.py +++ b/aiohasupervisor/homeassistant.py @@ -32,7 +32,7 @@ async def options(self, options: HomeAssistantOptions) -> None: async def update(self, options: HomeAssistantUpdateOptions | None = None) -> None: """Update Home Assistant.""" await self._client.post( - "core/update", json=options.to_dict() if options else None + "core/update", json=options.to_dict() if options else None, timeout=None ) async def restart(self, options: HomeAssistantRestartOptions | None = None) -> None: diff --git a/aiohasupervisor/host.py b/aiohasupervisor/host.py index b1e7401..7116617 100644 --- a/aiohasupervisor/host.py +++ b/aiohasupervisor/host.py @@ -1,6 +1,7 @@ """Host client for supervisor.""" from .client import _SupervisorComponentClient +from .const import TIMEOUT_60_SECONDS from .models.host import ( HostInfo, HostOptions, @@ -22,7 +23,9 @@ async def info(self) -> HostInfo: async def reboot(self, options: RebootOptions | None = None) -> None: """Reboot host.""" await self._client.post( - "host/reboot", json=options.to_dict() if options else None + "host/reboot", + json=options.to_dict() if options else None, + timeout=TIMEOUT_60_SECONDS, ) async def shutdown(self, options: ShutdownOptions | None = None) -> None: diff --git a/aiohasupervisor/os.py b/aiohasupervisor/os.py index 593c76e..5d7d797 100644 --- a/aiohasupervisor/os.py +++ b/aiohasupervisor/os.py @@ -26,7 +26,7 @@ async def info(self) -> OSInfo: async def update(self, options: OSUpdate | None = None) -> None: """Update OS.""" await self._client.post( - "os/update", json=options.to_dict() if options else None + "os/update", json=options.to_dict() if options else None, timeout=None ) async def config_sync(self) -> None: diff --git a/aiohasupervisor/resolution.py b/aiohasupervisor/resolution.py index 572b1e2..4b7d4eb 100644 --- a/aiohasupervisor/resolution.py +++ b/aiohasupervisor/resolution.py @@ -34,7 +34,7 @@ async def run_check(self, check: CheckType | str) -> None: async def apply_suggestion(self, suggestion: UUID) -> None: """Apply a suggestion.""" - await self._client.post(f"resolution/suggestion/{suggestion.hex}") + await self._client.post(f"resolution/suggestion/{suggestion.hex}", timeout=None) async def dismiss_suggestion(self, suggestion: UUID) -> None: """Dismiss a suggestion.""" diff --git a/aiohasupervisor/root.py b/aiohasupervisor/root.py index c3b5401..4fbddbc 100644 --- a/aiohasupervisor/root.py +++ b/aiohasupervisor/root.py @@ -2,7 +2,7 @@ from typing import Self -from aiohttp import ClientSession +from aiohttp import ClientSession, ClientTimeout from .addons import AddonsClient from .backups import BackupsClient @@ -25,11 +25,10 @@ def __init__( self, api_host: str, token: str, - request_timeout: int = 10, session: ClientSession | None = None, ) -> None: """Initialize client.""" - self._client = _SupervisorClient(api_host, token, request_timeout, session) + self._client = _SupervisorClient(api_host, token, session) self._addons = AddonsClient(self._client) self._os = OSClient(self._client) self._backups = BackupsClient(self._client) @@ -98,7 +97,7 @@ async def info(self) -> RootInfo: async def refresh_updates(self) -> None: """Refresh updates.""" - await self._client.post("refresh_updates") + await self._client.post("refresh_updates", timeout=ClientTimeout(total=300)) async def available_updates(self) -> list[AvailableUpdate]: """Get available updates.""" diff --git a/aiohasupervisor/store.py b/aiohasupervisor/store.py index c3cb392..71ebc9e 100644 --- a/aiohasupervisor/store.py +++ b/aiohasupervisor/store.py @@ -47,14 +47,16 @@ async def addon_documentation(self, addon: str) -> str: async def install_addon(self, addon: str) -> None: """Install an addon.""" - await self._client.post(f"store/addons/{addon}/install") + await self._client.post(f"store/addons/{addon}/install", timeout=None) async def update_addon( self, addon: str, options: StoreAddonUpdate | None = None ) -> None: """Update an addon to latest version.""" await self._client.post( - f"store/addons/{addon}/update", json=options.to_dict() if options else None + f"store/addons/{addon}/update", + json=options.to_dict() if options else None, + timeout=None, ) async def reload(self) -> None: diff --git a/aiohasupervisor/supervisor.py b/aiohasupervisor/supervisor.py index 520263b..9188ff7 100644 --- a/aiohasupervisor/supervisor.py +++ b/aiohasupervisor/supervisor.py @@ -1,5 +1,7 @@ """Supervisor client for supervisor.""" +from aiohttp import ClientTimeout + from .client import _SupervisorComponentClient from .const import ResponseType from .models.supervisor import ( @@ -15,7 +17,11 @@ class SupervisorManagementClient(_SupervisorComponentClient): async def ping(self) -> None: """Check connection to supervisor.""" - await self._client.get("supervisor/ping", response_type=ResponseType.NONE) + await self._client.get( + "supervisor/ping", + response_type=ResponseType.NONE, + timeout=ClientTimeout(total=15), + ) async def info(self) -> SupervisorInfo: """Get supervisor info.""" @@ -35,7 +41,9 @@ async def update(self, options: SupervisorUpdateOptions | None = None) -> None: latest version and ignore that field. """ await self._client.post( - "supervisor/update", json=options.to_dict() if options else None + "supervisor/update", + json=options.to_dict() if options else None, + timeout=None, ) async def reload(self) -> None: