Files
adguard-control-hub/custom_components/adguard_hub/services.py
Rafal Zielinski e0edf6f865
Some checks failed
🧪 Integration Testing / 🔧 Test Integration (2025.9.4, 3.13) (push) Failing after 19s
🛡️ Code Quality & Security Check / 🔍 Code Quality Analysis (push) Failing after 19s
fix: fixes
Signed-off-by: Rafal Zielinski <sq4ind@gmail.com>
2025-09-28 15:46:21 +01:00

188 lines
7.4 KiB
Python

"""Service implementations for AdGuard Control Hub integration."""
import asyncio
import logging
from typing import Any, Dict
import voluptuous as vol
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from .api import AdGuardHomeAPI
from .const import (
DOMAIN,
BLOCKED_SERVICES,
ATTR_CLIENT_NAME,
ATTR_SERVICES,
ATTR_DURATION,
ATTR_CLIENTS,
)
_LOGGER = logging.getLogger(__name__)
# Service schemas
SCHEMA_BLOCK_SERVICES = vol.Schema({
vol.Required(ATTR_CLIENT_NAME): cv.string,
vol.Required(ATTR_SERVICES): vol.All(cv.ensure_list, [vol.In(BLOCKED_SERVICES.keys())]),
})
SCHEMA_EMERGENCY_UNBLOCK = vol.Schema({
vol.Required(ATTR_DURATION): cv.positive_int,
vol.Optional(ATTR_CLIENTS, default=["all"]): vol.All(cv.ensure_list, [cv.string]),
})
SERVICE_BLOCK_SERVICES = "block_services"
SERVICE_UNBLOCK_SERVICES = "unblock_services"
SERVICE_EMERGENCY_UNBLOCK = "emergency_unblock"
SERVICE_ADD_CLIENT = "add_client"
SERVICE_REMOVE_CLIENT = "remove_client"
SERVICE_BULK_UPDATE_CLIENTS = "bulk_update_clients"
class AdGuardControlHubServices:
"""Handle services for AdGuard Control Hub."""
def __init__(self, hass: HomeAssistant):
"""Initialize the services."""
self.hass = hass
self._emergency_unblock_tasks: Dict[str, asyncio.Task] = {}
def register_services(self) -> None:
"""Register all services."""
self.hass.services.register(
DOMAIN,
SERVICE_BLOCK_SERVICES,
self.block_services,
schema=SCHEMA_BLOCK_SERVICES,
)
self.hass.services.register(
DOMAIN,
SERVICE_UNBLOCK_SERVICES,
self.unblock_services,
schema=SCHEMA_BLOCK_SERVICES,
)
self.hass.services.register(
DOMAIN,
SERVICE_EMERGENCY_UNBLOCK,
self.emergency_unblock,
schema=SCHEMA_EMERGENCY_UNBLOCK,
)
# Additional services would go here
self.hass.services.register(DOMAIN, SERVICE_ADD_CLIENT, self.add_client)
self.hass.services.register(DOMAIN, SERVICE_REMOVE_CLIENT, self.remove_client)
self.hass.services.register(DOMAIN, SERVICE_BULK_UPDATE_CLIENTS, self.bulk_update_clients)
def unregister_services(self) -> None:
"""Unregister all services."""
services = [
SERVICE_BLOCK_SERVICES,
SERVICE_UNBLOCK_SERVICES,
SERVICE_EMERGENCY_UNBLOCK,
SERVICE_ADD_CLIENT,
SERVICE_REMOVE_CLIENT,
SERVICE_BULK_UPDATE_CLIENTS,
]
for service in services:
if self.hass.services.has_service(DOMAIN, service):
self.hass.services.remove(DOMAIN, service)
async def block_services(self, call: ServiceCall) -> None:
"""Block services for a specific client."""
client_name = call.data[ATTR_CLIENT_NAME]
services = call.data[ATTR_SERVICES]
_LOGGER.info("Blocking services %s for client %s", services, client_name)
for entry_data in self.hass.data[DOMAIN].values():
api: AdGuardHomeAPI = entry_data["api"]
try:
client = await api.get_client_by_name(client_name)
if client:
current_blocked = client.get("blocked_services", {})
current_services = current_blocked.get("ids", []) if isinstance(current_blocked, dict) else current_blocked or []
updated_services = list(set(current_services + services))
await api.update_client_blocked_services(client_name, updated_services)
_LOGGER.info("Successfully blocked services for %s", client_name)
except Exception as err:
_LOGGER.error("Failed to block services for %s: %s", client_name, err)
async def unblock_services(self, call: ServiceCall) -> None:
"""Unblock services for a specific client."""
client_name = call.data[ATTR_CLIENT_NAME]
services = call.data[ATTR_SERVICES]
_LOGGER.info("Unblocking services %s for client %s", services, client_name)
for entry_data in self.hass.data[DOMAIN].values():
api: AdGuardHomeAPI = entry_data["api"]
try:
client = await api.get_client_by_name(client_name)
if client:
current_blocked = client.get("blocked_services", {})
current_services = current_blocked.get("ids", []) if isinstance(current_blocked, dict) else current_blocked or []
updated_services = [s for s in current_services if s not in services]
await api.update_client_blocked_services(client_name, updated_services)
_LOGGER.info("Successfully unblocked services for %s", client_name)
except Exception as err:
_LOGGER.error("Failed to unblock services for %s: %s", client_name, err)
async def emergency_unblock(self, call: ServiceCall) -> None:
"""Emergency unblock - temporarily disable protection."""
duration = call.data[ATTR_DURATION]
clients = call.data[ATTR_CLIENTS]
_LOGGER.warning("Emergency unblock activated for %s seconds", duration)
for entry_data in self.hass.data[DOMAIN].values():
api: AdGuardHomeAPI = entry_data["api"]
try:
if "all" in clients:
await api.set_protection(False)
task = asyncio.create_task(self._delayed_enable_protection(api, duration))
self._emergency_unblock_tasks[f"{api.host}:{api.port}"] = task
except Exception as err:
_LOGGER.error("Failed to execute emergency unblock: %s", err)
async def _delayed_enable_protection(self, api: AdGuardHomeAPI, delay: int) -> None:
"""Re-enable protection after delay."""
await asyncio.sleep(delay)
try:
await api.set_protection(True)
_LOGGER.info("Emergency unblock expired - protection re-enabled")
except Exception as err:
_LOGGER.error("Failed to re-enable protection: %s", err)
async def add_client(self, call: ServiceCall) -> None:
"""Add a new client."""
client_data = dict(call.data)
_LOGGER.info("Adding new client: %s", client_data.get("name"))
for entry_data in self.hass.data[DOMAIN].values():
api: AdGuardHomeAPI = entry_data["api"]
try:
await api.add_client(client_data)
_LOGGER.info("Successfully added client: %s", client_data.get("name"))
except Exception as err:
_LOGGER.error("Failed to add client: %s", err)
async def remove_client(self, call: ServiceCall) -> None:
"""Remove a client."""
client_name = call.data.get("name")
_LOGGER.info("Removing client: %s", client_name)
for entry_data in self.hass.data[DOMAIN].values():
api: AdGuardHomeAPI = entry_data["api"]
try:
await api.delete_client(client_name)
_LOGGER.info("Successfully removed client: %s", client_name)
except Exception as err:
_LOGGER.error("Failed to remove client %s: %s", client_name, err)
async def bulk_update_clients(self, call: ServiceCall) -> None:
"""Update multiple clients matching a pattern."""
_LOGGER.info("Bulk update clients called")
# Implementation would go here