fix: fixes
Some checks failed
Integration Testing / Test Integration (2025.9.4, 3.13) (push) Failing after 38s
Code Quality Check / Code Quality Analysis (push) Successful in 15s

Signed-off-by: Rafal Zielinski <sq4ind@gmail.com>
This commit is contained in:
2025-09-28 15:58:07 +01:00
parent e0edf6f865
commit 86f60e72b7
21 changed files with 147 additions and 466 deletions

View File

@@ -10,23 +10,27 @@ from .const import API_ENDPOINTS
_LOGGER = logging.getLogger(__name__)
# Custom exceptions
class AdGuardHomeError(Exception):
"""Base exception for AdGuard Home API."""
pass
class AdGuardConnectionError(AdGuardHomeError):
"""Exception for connection errors."""
pass
class AdGuardAuthError(AdGuardHomeError):
"""Exception for authentication errors."""
pass
class AdGuardNotFoundError(AdGuardHomeError):
"""Exception for not found errors."""
pass
class AdGuardHomeAPI:
"""API wrapper for AdGuard Home."""
@@ -71,7 +75,7 @@ class AdGuardHomeAPI:
return self._session
async def _request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
"""Make an API request with comprehensive error handling."""
"""Make an API request."""
url = f"{self.base_url}{endpoint}"
headers = {"Content-Type": "application/json"}
auth = None
@@ -84,11 +88,8 @@ class AdGuardHomeAPI:
method, url, json=data, headers=headers, auth=auth
) as response:
# Handle different HTTP status codes
if response.status == 401:
raise AdGuardAuthError("Authentication failed - check username/password")
elif response.status == 403:
raise AdGuardAuthError("Access forbidden - insufficient permissions")
raise AdGuardAuthError("Authentication failed")
elif response.status == 404:
raise AdGuardNotFoundError(f"Endpoint not found: {endpoint}")
elif response.status >= 500:
@@ -96,24 +97,20 @@ class AdGuardHomeAPI:
response.raise_for_status()
# Handle empty responses
if response.status == 204 or not response.content_length:
return {}
try:
return await response.json()
except aiohttp.ContentTypeError:
# Handle non-JSON responses
text = await response.text()
_LOGGER.warning("Non-JSON response received: %s", text)
return {"response": text}
except asyncio.TimeoutError as err:
raise AdGuardConnectionError(f"Timeout connecting to AdGuard Home: {err}")
raise AdGuardConnectionError(f"Timeout: {err}")
except ClientError as err:
raise AdGuardConnectionError(f"Client error: {err}")
except Exception as err:
_LOGGER.error("Unexpected error communicating with AdGuard Home: %s", err)
raise AdGuardHomeError(f"Unexpected error: {err}")
async def test_connection(self) -> bool:
@@ -121,8 +118,7 @@ class AdGuardHomeAPI:
try:
await self._request("GET", API_ENDPOINTS["status"])
return True
except Exception as err:
_LOGGER.debug("Connection test failed: %s", err)
except Exception:
return False
async def get_status(self) -> Dict[str, Any]:
@@ -144,7 +140,6 @@ class AdGuardHomeAPI:
async def add_client(self, client_data: Dict[str, Any]) -> Dict[str, Any]:
"""Add a new client configuration."""
# Validate required fields
if "name" not in client_data:
raise ValueError("Client name is required")
if "ids" not in client_data or not client_data["ids"]:
@@ -155,9 +150,9 @@ class AdGuardHomeAPI:
async def update_client(self, client_data: Dict[str, Any]) -> Dict[str, Any]:
"""Update an existing client configuration."""
if "name" not in client_data:
raise ValueError("Client name is required for update")
raise ValueError("Client name is required")
if "data" not in client_data:
raise ValueError("Client data is required for update")
raise ValueError("Client data is required")
return await self._request("POST", API_ENDPOINTS["clients_update"], client_data)
@@ -183,15 +178,13 @@ class AdGuardHomeAPI:
return client
return None
except Exception as err:
_LOGGER.error("Failed to get client %s: %s", client_name, err)
except Exception:
return None
async def update_client_blocked_services(
self,
client_name: str,
blocked_services: list,
schedule: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""Update blocked services for a specific client."""
if not client_name:
@@ -201,21 +194,11 @@ class AdGuardHomeAPI:
if not client:
raise AdGuardNotFoundError(f"Client '{client_name}' not found")
# Prepare the blocked services data with proper structure
if schedule:
blocked_services_data = {
"ids": blocked_services,
"schedule": schedule
}
else:
blocked_services_data = {
"ids": blocked_services,
"schedule": {
"time_zone": "Local"
}
}
blocked_services_data = {
"ids": blocked_services,
"schedule": {"time_zone": "Local"}
}
# Update the client with new blocked services
update_data = {
"name": client_name,
"data": {
@@ -226,37 +209,6 @@ class AdGuardHomeAPI:
return await self.update_client(update_data)
async def toggle_client_service(
self, client_name: str, service_id: str, enabled: bool
) -> Dict[str, Any]:
"""Toggle a specific service for a client."""
if not client_name or not service_id:
raise ValueError("Client name and service ID are required")
client = await self.get_client_by_name(client_name)
if not client:
raise AdGuardNotFoundError(f"Client '{client_name}' not found")
# Get current blocked services
blocked_services = client.get("blocked_services", {})
if isinstance(blocked_services, dict):
service_ids = blocked_services.get("ids", [])
else:
# Handle legacy format (direct list)
service_ids = blocked_services if blocked_services else []
# Update the service list
if enabled and service_id not in service_ids:
service_ids.append(service_id)
elif not enabled and service_id in service_ids:
service_ids.remove(service_id)
return await self.update_client_blocked_services(client_name, service_ids)
async def get_blocked_services(self) -> Dict[str, Any]:
"""Get available blocked services."""
return await self._request("GET", API_ENDPOINTS["blocked_services_all"])
async def close(self) -> None:
"""Close the API session if we own it."""
if self._own_session and self._session: